Random Stuff

Reading Much Ado About Nothing by Shakespeare.

Reading select works of 川端 康成.

Reading 中日交流标准日本语 初级 上 (1988 book).

Reading An introduction to homological algebra (1994 book) by Charles A. Weibel.

Reading 作为意志和表象的世界 (translation by 石冲白 of volume 1 of 1859 book Die Welt als Wille und Vorstellung by Arthur Schopenhauer).

Watching Summer Time Rendering (2022 anime).

Watching FX戦士くるみちゃん and とある暗部の少女共棲 (current animes).

Playing 蒼の彼方のフォーリズム (2014 visual novel).

2026-10-23

I'm not sure what the "now" in now.vursc.org should mean: right now, today, this week, this month, or this year? Should archives live here? Different timeframes benefit different people, and I often look at this page to figure out what to do next (better be doing things I say I'm doing). I'll just keep it the way it is.

For quite a few use cases "sexprs with optional keys" makes sense. For example, an URL may be represented in parsed form as any of the following (the outer pair of brackets can be made implicit for the common case):

[https,example.com,/]
[https example.com /]
[scheme=https host=example.com path=/]
[scheme=https host=example.com path=/ auth= query= frag=]
[scheme=https host=example.com path=/ auth=[basic user=foo pass=bar] query= frag=]

2026-10-22

TIL Japanese postal mark U+3012 "〒". There's also U+3020 postal mark face "〠" and U+303C masu mark "〼".

2026-10-21

Skimmed GEFS, A Good Enough File System (PDF).

Gave up watching 魔法少女にあこがれて (2024 anime) after 4 episodes.

My first batch of CDs arrived today. Gotta spend some time going through them.

Sandboxing MuPDF turns out to be more complicated than I thought. The simple approach is to turn each PDF operation (e.g. page render) into an RPC via a pair of pipes, but if the MuPDF process fails to read from the request pipe or write to the response pipe the main process may freeze, and one must kill not only the main process but also the MuPDF process manually (killing the latter would not be necessary if it eventually writes to the response pipe and dies on SIGPIPE, but there's no such guarantee), which is not great, especially given that this may result from a PDF that's merely hard to render and not maliciously crafted. Thus, one has to make the main thread fully asynchronous and poll the MuPDF process like it polls for inotify events, but this is really tedious to code in C, and even if someone produced this code with an LLM, hard to verify correctness. I'm not sure whether the extra complexity is worth it, and maybe I somehow missed a simpler approach, as in this very specific case the only asynchronous event handler kills both processes.

It really sucks that poll does not compose. The following seems to be the simplest solution. A worker thread communicates with the MuPDF process. When the main thread wants to do I/O, it writes the operation description (read or write, buffer and size) to memory and increments a semaphore X. The worker thread waits on X, performs the I/O, increments another semaphore Y, and calls glfwPostEmptyEvent. The main thread calls glfwWaitEvents in a loop until Y is nonzero. If the operation completes normally, the loop terminates and the main thread proceeds; otherwise the key callback kills the MuPDF process and then the main process. Since the only asynchronous event handler diverges, the main thread doesn't care about events received here. (This is not really true: the other asynchronous event is from inotify, for which we need to reload the PDF file by killing the MuPDF process, spawning a new one and sending the PDF over.)

2026-10-20

Made an icon for my hobby operating system GraciOS (GROS, pun intended), which currently has exactly zero lines of code, and for which I want to build a programming language and compiler. The radii of the two circles are 45 units and 35 units, and all strokes are 10 units wide. One end of the diagonal stroke extends 15 units from the center, and the other end is part of the outer circle.

Yesterday I was caught in some surveillance crap and, although I'm not supposed to have strong moral stances (not in equilibrium, and in another sense not specific), I find it pretty annoying. Crap is crap and enough is enough; every second alive contributes to proving that the system works. (Does that need proof at all? We all know that some people survived famines, plagues, and the world wars.) My feeling good apparently requires attention to all the good causes, which is not great when the goal is to just keep calm and carry on. "It's not that bad, they're quite happy even; let's ditch privacy and democracy too" I hear them say. (Not that it's quite working.) Stoicism doesn't help here, as the distinction between what one can change and what one cannot is quite arbitrary: given one spoon and two causes that cost one spoon each, one may decide which to spend the spoon on and the other becomes "what one cannot change". Still confused.

The other day I was thinking about making my own drawing program and only then did I realize that "update canvas with stroke" is a pretty complicated operation. Given a pen description (texture and stuff) and an array of position and pressure samples, one has to somehow simulate the effect of a pen travelling along the path, while avoiding artifacts near the sampled points. Apparently people did figure this out; maybe dig this up someday.

2026-10-17

Watched Terminator: Dark Fate (2019 film).

2026-10-15

Watched Terminator Salvation (2009 film).

Gave up watching 攻殻機動隊 STAND ALONE COMPLEX (2002-2003 anime) after the first 12 episodes. I find it boring.

Do you know what happens if you set ELF PT_INTERP to /proc/self/exe?

I've been editing my online writings for clarity since forever, but now that I think about it, precisely zero people that I don't know in real life emailed me about my online writings, so it has had no tangible utility. Maybe I should stop doing it.

2026-10-12

Finished reading a translation of Crime and Punishment (1866 book by Fyodor Dostoevsky).

In a mobile application (glorified Web page) I was using just now, the content is 2-3 millimeters taller than the viewport, resulting in that much scroll. Like, this doesn't make people die (earlier than they all eventually would anyway, that is), except possibly indirectly through induced stress, but why?

And the "back" button in the top-left corner that appears in a slightly different place on each page...

2026-10-11

Finished watching ぐらんぶる season 3 (2026 anime).

Finished watching 転校先の清楚可憐な美少女が、昔男子と思って一緒に遊んだ幼馴染だった件 (2026 anime).

2026-10-10

Watched Fight Club (1999 film). Very interesting. Very good sceneplay and narration. Probably not the best film to watch now though.

2026-10-09

Spent three weeks in hospital after a failed attempt. There was a creepy guy who played, every day, in the public place, in a loop, some awful ~30s audio clip, and it was so annoying I had to escape to the other end of the corridor. I couldn't use my laptop, so instead read translations of:

2026-09-17

Read 后现代状态 (translation by 车槿山 of 1979 book La condition postmoderne by Jean-François Lyotard). I like it. It makes sense and is quite relevant.

2026-09-16

I communicate to humans no better than to trees.

Does (little yet) too much culture admit the treatment of more culture, or should I just learn to drink?

2026-09-13

Read “零度”日本——陷入“关系性贫困”的年轻一代 (2025 book by 王钦). Of eight essays, the first four share a common theme — the non-communication, or denial or even impossibility of communication, among mainstream culture and a plethora of emerging subcultures. It relates and clarifies some of my confusions and frustrations, though I still feel very much overwhelmed by the postmodern world. The other four are much less worth reading (to me). Later I'd like to read about youth in post-WWII France and in the US during and after the Vietnam war.

2026-09-11

Re Humans are Bad at URLs and Fonts Don't Matter (2020, via, via): Nope, it is the coders' fault: the Security Considerations section of RFC 3490 mentions homoglyph attacks ("confusion between characters that are visually similar"). RFC 5890 says "[T]here are no comprehensive technical solutions to the problems of confusable characters", but obviously there is one: display the raw Unicode code points. (I don't understand UAX15: how could one normalize a Unicode string without first decoding it into a sequence of code points? Any byte sequence is by definition a Unicode string with 8-bit code units.)

I have two views about the world: the pessimistic one is that everything is broken and nobody seems to care; the optimistic one is that bad things probably are fake (i.e. don't really exist) anyway, and so there's nothing to worry about.

Read 个人的体验 (translation by 王中忱 of 1964 book 個人的な体験 by 大江 健三郎). Most of it is pretty good, but the ending (section 13 and the epilog) is abrupt and destroys the mood, and I consider it a mistake.

2026-09-08

Was feeling uncomfortable and couldn't sleep so headed outside thinking I'd find a bar and talk to some real people™. Ended up in a convenience store with potato chips, instant noodles, and coke. Still made me feel better.

It's still awful back here though.

I learned of out of order HTML patching (also) a few days ago. Here's a counter (that works in Chromium but not Firefox or Safari):

{
    printf 'HTTP/1.1 200 OK\r\n'
    printf 'Transfer-Encoding: chunked\r\n'
    printf 'Content-Type: text/html\r\n'
    printf '\r\n'
    printf '25\r\n'  # chunk length in hex
    printf '<div><?start name="1">0<?end></div>\r\n'
    printf '\r\n'

    sleep 1
    for ((i=1,j=2;i<50;++i,++j)); do
        sleep .1
        printf '%x\r\n' $((52+2*${#i}+${#j}))
        printf '<template for="%s"><?start name="%s">%s<?end></template>\r\n' $i $j $i
        printf '\r\n'
    done

    printf '0\r\n'
    printf '\r\n'
} | nc -l -q0 127.0.0.1 8080

(nc -C translates LF to CRLF, but anyway.) I think I could play Bad Apple this way, or just by prefixing each frame with a <style> that hides the previous frame (nope: <style> is not allowed in <body>) hiding everything in <body> except :last-child.

2026-09-06

Pretense... It's all make-believe.

Watched 人類は衰退しました (2012 anime). I find it confusing; the anime adaptation may be the culprit. I know of a few other works by 田中 ロメオ...

(Firefox would remove a newline used in place of the space between "田中" and "ロメオ", presumably so CJK writers could wrap their lines, but I think would break old HTML documents that relied on the space. Automatic line joining/breaking can't be nice and correct.)

2026-08-31

Has anyone built and programmed a machine to build itself given steel blocks, plates, and beams, aluminium blocks and plates, wires, and a set of PCB assemblies?

The following paragraph is revealed word by word as you click through them. I used checkboxes but <details> should work too:

2026-08-29

TIL U+3315 ㌕ "kilogram" (see also). The block also contains U+337B ㍻, U+337C ㍼, U+337D ㍽, and U+337E ㍾, but U+32FF ㋿ went to another block.

zathura-pdf-mupdf seems to always render the whole page at the specified zoom level: a single A4 page takes 1.3 to 1.4 GB at max zoom (10x) on my ~190 dpi display. Testing shows that it uses this DPI even when started on another display, which is unexpected. Multiple monitors with different DPIs work somewhat on Windows but just suck anyway.

Given the logo, I really expected UFO to be an sexp format than XML-in-ZIP...

Is the (simplified) subroutinization problem NP-complete?

2026-08-26

I'm tired of all this "Child Protection" bullshit; just can't have nice things though.

2026-08-25

This page contains some JavaScript that renders an SVG graph from hard-coded data. Just found that the output SVG is smaller than the script and compresses better, and I could tweak it to compress even better. Writing SVG directly makes raw data harder to access though, and I don't want a build step or to make this page dynamic, so I'll keep it as is. That's some 300 to 400 bytes over the wire for me being lazy.

Update 2026-08-29: Actually, I'm wasting another ~1.3 KB by having Caddy compress this page using a default-ish level than zstd -19, or even better brotli (saves another ~2.3 KB). Not gonna do that either: a precompressed file has priority over the original, so if it goes stale updates to the original are silently masked.

2026-08-24

I'm surprised I'm surprised streaming music on my phone from my laptop feels so nice. Guess it'd be even better with 9p over TLS (with a client certificate) over SCTP instead of regular HTTPS. Connecting with a (server address, port, certificate; client certificate) quadruple doesn't seem to be a thing in "regular" Web browsers...

2026-08-23

It'd be cool were libavformat called libavmudex.

A pirate website offers some JavaScript to download selected files of an item and save them as a ZIP. The speed meter and progress bars are flaky: despite continuous network traffic, most of the time no speed is shown and progress bars freeze, and sometimes the speeds shown are absurdly fast and progress bars jump wildly. I just looked into it and it's quite simple. Files of moderate size, up to about 400 MB, are cut into 16 equally-sized chunks and downloaded sequentially. When a chunk download is finished, the progress bar and a per-second counter for bytes received is updated. My network is somewhat slow so downloading, say, a 10 MB chunk can take multiple seconds, and the speed meter stays at zero until the download is finished and it goes to 10 MB/s for one second. Worse, larger files are not chunked, so a 600 MB file leads to a 600 MB/s spike in speed and its progress bar is totally useless, only ever being at 0% or 100%.

Another annoying thing is seemingly random CORS failures, which force me to restart these multi-GB downloads. Not enough so to make me write a shell script for it though. It just failed three times in a row so I did a quick Python script to feed URLs into curl — the listing is a JSON tree and I don't know how to make jq recurse with an extra argument (the current prefix).

2026-08-20

Watched Terminator Genisys (2015 film).

2026-08-19

TIL using curl's --parallel with --fail-early terminates partial downloads on errors, including those specified before the failing one. See also. The story: I needed to download some sequentially numbered files but didn't know how many, so I fed curl 100 URLs at a time and passed --fail so it treats a 404 as an error, hoping that once it gets a 404 it would stop sending requests, which it did, but it also canceled the other active downloads, which (I think) removed or truncated up to --parallel-max files. Now I must clean up this mess, and I cannot quite automate it because intermittent network conditions... Not fun.

2026-08-17

This is ridiculous.

# from traffic log
content-signature-2.cdn.mozilla.net
firefox-settings-attachments.cdn.mozilla.net
firefox.settings.services.mozilla.com
push.services.mozilla.com

Why the split between mozilla.com and mozilla.net? Just stick with one maybe? (I know I could disable them. Also, that page has less text than this one and requires JavaScript for some reason.)

2026-08-16

Watched The Internship (2013 film). Only realized that sqrt(17) isn't anywhere close to 4.23 when I went to get some water; guess I'll be like that when drunk too. Nice comedy if you can stand the bullshit, only not enough fighting in that dance club.

Also, the credits comes in some interesting font...

2026-08-12

Here's a way to lazy-load images by (not quite) abusing <details>:

<style>
details { margin: 1em 0; }
details[open] summary { display: none; }
summary, img {
    display: block;
    margin: auto;
    width: min(80%, 80vh * var(--r));
    aspect-ratio: var(--r);

    background: #eee;
    text-align: center;
    align-content: center;
}
</style>

<details style="--r:800/600">
<summary>Load image (123K)</summary>
<img loading="lazy" src="image.jpg" alt="sample image">
</details>

Firefox doesn't currently support typed attr() in CSS so I have to define a CSS variable on <details>. I don't know how to get the largest size given a maximum width, a maximum height, and a fixed aspect ratio, thus the min().

Without JavaScript, the image would still load eagerly, for privacy reasons. This construct is not too useful since most probably the user wants to see a scaled-down version first, in which case the "link to full-sized image" idiom is well-established.

2026-08-11

Finished the 鳥沢 みさき plotline, which occupied much of my time awake today. It's much more interesting. The plot is actually quite boring if I think about it, but it's relatable and feels nice for some reason.

Been eating instant Udon for a few nights. Nice alternative to regular instant noodles, but still very unhealthy. There're few if any food shops open this late, let alone affordable ones. I've been thinking "what for", but one has to get by; there's no "pristine state of human nature" to be disrupted by the socio-industrial complex anyway.

2026-08-07

Been playing 蒼の彼方のフォーリズム (2014 visual novel); just finished the 市ノ瀬 莉佳 plotline, despite having tried to enter that of 鳶沢 みさき.

2026-08-06

mailbox (the email service provider) shows a "Spam" and a "Trash" folder in the Web UI, but "Spam" is actually called "Junk": the name shows up in "Settings → Spam and trash → Spam Settings → Spam Emails → Route to". If I put an email in "Junk" in a local maildir and sync via IMAP, it goes in "Spam" in the Web UI; if I put it in "Spam" instead it goes in, well, another "Spam", in "My folders". I guess mailbox can't fix it anymore though.

Just pointing out that some blog post I just read loads about 15 KB of HTML, a 30 KB image, about three times that much CSS, and a 250 KB GitHub avatar that isn't even on the page but a <link rel="preload">. Must be really confident I'd visit the homepage to see that avatar.

2026-08-03

Watched みるタイツ (2019 anime).

My (crappy) PDF viewer depends on MuPDF to actually render PDFs, but it is complex, has lots of dependencies, and deals with images, fonts, and apparently the Internet, so it probably supports Remote Code Execution, so I better sandbox it. Since rendering PDFs is mostly "pure computation", SECCOMP_SET_MODE_STRICT works, but I have to avoid libc's malloc and stdout. (Why would printf call fstat?) fz_new_context reads /etc/localtime for some reason, so it has to go before the Seccomp call, which is weird but fine, since MuPDF's dependencies are initialized outside Seccomp anyway.

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/seccomp.h>
#include <mupdf/fitz.h>

char data[1<<24], pool[1<<24], *top = pool;

void *mymalloc(void *_, size_t n) { void *t = top; n = (n + 15) & ~15; top += n; return t; }
void *myrealloc(void *_, void *p, size_t n) { void *t = mymalloc(_, n); if (p) memcpy(t, p, n); return t; }
void myfree(void *_, void *p) {}

int main(void) {
    fz_alloc_context actx = { 0, mymalloc, myrealloc, myfree };
    fz_buffer *buf;
    fz_context *ctx;
    fz_document *doc;
    fz_pixmap *pix;
    int size;
    char ihdr[100];

    ctx = fz_new_context(&actx, 0, FZ_STORE_UNLIMITED);
    syscall(SYS_seccomp, SECCOMP_SET_MODE_STRICT, 0, 0);
    fz_register_document_handlers(ctx);
    size = read(0, data, sizeof(data));
    buf = fz_new_buffer_from_shared_data(ctx, data, size);
    doc = fz_open_document_with_buffer(ctx, data, buf);
    pix = fz_new_pixmap_from_page_number(ctx, doc, 0, fz_identity, fz_device_rgb(ctx), 0);
    write(1, ihdr, sprintf(ihdr, "P6\n%d %d\n255\n", pix->w, pix->h));
    write(1, pix->samples, pix->w * pix->h * 3);

    fz_drop_context(ctx);   // I think this does nothing
    syscall(60, 0);
}

I probably should only allow LANDLOCK_ACCESS_FS_READ_FILE before loading MuPDF. I'm not sure if no_new_privs does anything then. (08-06: no_new_privs is required for landlock_restrict_self.)

2026-08-01

Watched Don't Take the Black Pill by Andrew Kelley. I certainly do feel disillusioned, and would go further than "computers were a mistake" to say "life was a mistake". I don't have much hope now that the world is... Well, my favourite websites turn into shit, adopt Cloudflare or Anubis, or get shut down. People lie or bullshit all the time. The Internet is like "the thing that never works but somehow people all depend on". Not a great time to have faith.

Also, I hope I'll watch Matrix again.

I've been neglecting Anki for three weeks. Not good.

2026-07-31

Re-watched ギルティクラウン (2011-2012 anime). Good memories... Hope to watch it again sometime.

2026-07-27

Reminder to self: the law mostly works because whoever discloses or exploits a bug gets thrown in jail. Or not.

2026-07-26

I have a USB isolator-hub, an STLINK (cheap clone), a mouse receiver, and a thumb drive. The hub has four device indicator LEDs (one for each downstream port), which agree with lsusb and /sys/bus/usb/devices/ (simply drawing power doesn't light up the LEDs). Funny thing is, if I plug the STLINK into the hub while the hub is connected to my computer and nothing is connected downstream, the hub dies, in that it doesn't connect the STLINK nor the mouse receiver however I plug and unplug them, unless I unplug and replug the hub into my computer. (The hub doesn't die if anything is connected downstream when I plug in the STLINK.) But not really: while dead, the hub starts working again if I plug in the thumb drive, irrespective of whether the STLINK and/or the mouse receiver is plugged in. Wow.

I have no idea where the problem is: Linux, the host controller, the isolator, the hub, or the STLINK.

For some reason I did the STM32F103C8T6 blink LED thing again, and ran into the same problems as last time, so I might as well document it. First install stlink and arm-none-eabi-{binutils,gcc,gdb}. Make sure STLINK works with st-info --probe. To use GDB, start st-util and connect with target extended-remote localhost:4242 in the GDB shell. Useful GDB commands: x/i $pc, x/w addr, set *addr = val. Check the value of a hardware register after writing into it.

We toggle the LED manually, just to make sure everything works. The onboard LED is between PC13 and VCC. Download the datasheet and the reference manual (RM0008) for this chip. In the "memory mapping" section of the datasheet, look up the addresses of "RCC" and "Port C", in this case 0x40021000 and 0x40011000. In the "RCC registers" section of the manual, find "APB2 peripheral clock enable register"; we need to set the IOPCEN bit, or bit 4 of this register, offset 0x18 from the RCC itself, or at 0x40021018: set *0x40021018=0x10. This turns on "Port C", which drives PC13. Then, in the "GPIO registers" section of the manual, find "port configuration register high"; we need to make PC13 an output, so bits 23-20 should be 0011. (That's max speed 50 MHz. Maybe it consumes less power at lower speeds, but I don't care.) The reset value is 0x44444444, so set *0x40011004=0x44344444. Finally, we write to the "port output data register", specifically toggle bit 13: set *0x4001100c^=0x2000. Repeat a few times and the LED should blink.

It remains to make the chip do this itself. I'll use assembly since I don't want to mess with instruction encodings (I didn't enjoy x86), and I've never quite figured out how to make GCC do what I want. We're on a Cortex-M3 processor, which runs ARMv7-M, which runs Thumb instructions. (It's a bit complicated.) I'm also using the "port bit set/reset register" instead of the "port output data register" here:

.thumb
.cpu cortex-m3

_start: .globl _start
    ldr r0, rccaddr         @ enable Port C
    mov r1, #0x10
    str r1, [r0, #0x18]
    ldr r0, pcaddr          @ set PC13 output push-pull 50 MHz
    mov r1, #3
    lsl r1, r1, #20
    str r1, [r0, #0x04]
    mov r1, #1              @ r1 = set13, r2 = reset13
    lsl r1, r1, #13
    lsl r2, r1, #16
    mov r7, #1              @ r7 = delay count
    lsl r7, r7, #19
loop:
    str r1, [r0, #0x10]     @ pc13 high, led off
    mov r3, r7
    bl spin
    str r2, [r0, #0x10]     @ pc13 low, led on
    mov r3, r7
    bl spin
    b loop

spin:
    sub r3, r3, #1
    bne spin
    bx lr

.align 2                    @ 4-byte align
rccaddr: .word 0x40021000
pcaddr: .word 0x40011000

A few things:

Speaking of disassembly... here's the build script:

set -e
arm-none-eabi-gcc -nostdlib main.s
arm-none-eabi-objdump -d a.out
arm-none-eabi-objcopy -O binary a.out a.bin
{ printf '\0\0\0\0\x09\x00\x00\x08'; cat a.bin; } > a.img
st-flash --reset write a.img 0x08000000

The --reset doesn't seem to be necessary but anyway. The curious printf produces the first two entries of the interrupt vector table, the initial sp and pc. (I don't know why the initial sp is needed.) The initial pc has its lowest bit set to indicate Thumb mode. Simply prepending bytes can mess up absolute addresses; the "proper" way is to have more than two entries in the table, put it in its own section and use a linker script; but of course we're not doing that.

The address 0x08000000 is the start of flash, from the "memory mapping" section of the datasheet. When BOOT0 is zero, flash is also mapped to address zero, from where the processor reads these values. (Hence, an initial pc of 0x9 also works.)

Here's a hex dump in case I get a new board and just want a quick test:

00000000: 0000 0000 0900 0008 0b48 1021 8161 0b48
00000010: 0321 0905 4160 0121 4903 0a04 0127 bf04
00000020: 0161 3b1c 00f0 05f8 0261 3b1c 00f0 01f8
00000030: f6e7 013b fdd1 7047 0010 0240 0010 0140

And that's it for now.

2026-07-24

Just migrated my blog on Codeberg Pages to the new git-pages service. Took me a while to figure out that the branch must be named pages and that SHA-256 repos don't work, so I had to delete the repo and re-create it with SHA-1.

2026-07-15

It's not news that writing strictly conforming C is super hard, but I think I just found a new way to invoke UB: let CHAT_BIT be 8 as usual, but let int have 1 sign bit, 32 value bits, and 7 padding bits, then adding three uint32_t variables in a row may make an intermediate int addition overflow. Luckily, bit-precise integer types are exempt from integer promotion (I just learned about this), so with a normal 32-bit int type, unsigned _BitInt(31) doesn't suffer from this problem.

2026-07-11

Watched ゼロの使い魔F (2012 anime, season 4 of ゼロの使い魔).

Configured Wake-on-LAN on my PC using a systemd.link file and learned to use arp-scan to find its IP address from my server. While the IP address of my server may change as well, I assigned it a static IP address (in a presumably unused DHCP range — I hope there won't be a conflict) so I can always connect to it with an Ethernet cable. DDNS doesn't look quite simple enough to set up.

2026-07-09

Watched Terminator 3: Rise of the Machines (2003 film).

Here's a helper for using Vim with Fcitx5. Doesn't work for commands like r; see :h utf-8-char-arg. Put it in ~/.config/vim/im and type :ru im to load it.

set tenc=utf-8
let curim = 'us-keyboard'
au InsertEnter * call system('fcitx5-remote --check -s ' .. curim)
au InsertLeave * let curim = system('fcitx5-remote --check -n; fcitx5-remote --check -c')

2026-07-07

Watched ゼロの使い魔〜三美姫の輪舞〜 (2008 anime, season 3 of ゼロの使い魔), including the OVA 誘惑の砂浜. I heard that the anime is much worse than the original light novel though...

2026-07-05

Finished playing おにいちゃんコンティニュー!ゆうりとしーくれっとらぶ (2025 visual novel).

2026-07-03

Replaced a night's sleep with four cups of coffee for no reason. Feels fucking awesome. /s

Been learning Japanese vocabulary with Anki for about three months. It started fun but now feels more like a chore, as I spend way more time reviewing cards (and realizing how much I forget) than learning new cards, which to be fair I can't really complain as that's just how memory works. The bigger issue is that, recently, I skip it often and otherwise usually procrastinate till after midnight, for, uh, reasons, which is probably bad for retention, which makes it feel even less rewarding.

If your microphone doesn't work with Firefox: check about:config and make sure media.getusermedia.audio.max_channels is nonzero.

2026-07-02

Fixed my suitcase which broke a wheel about a year ago when I dragged it while going like thirty (km/h) on a bike and hit a speed bump. The wheels are made of plastic and mounted with screws pushing against another piece of plastic placed inside the suitcase. After the accident I took out the internal part to look for a replacement but failed, and then lost that part (silly me). Just now I found one for the external part but not the internal part, so I gave up and replaced the broken wheel with a good one from the diagonal position so at least I can drag the suitcase around. Problem is, the screw holes are so weak I'd strip them unless I pay extremely careful attention while driving the screws, which of course I didn't. Ugh. Hope it works.

It sucks that suitcase wheel mounts aren't standard.

2026-06-30

Watched ゼロの使い魔~双月の騎士~ (2007 anime, season 2 of ゼロの使い魔).

2026-06-29

Watched ゼロの使い魔 (2006 anime, season 1).

I've been thinking about a good way to think about things: to take as a fact that everything eventually goes to shit. It doesn't even need to be true: if something doesn't look like it's going to shit it's because your lifespan is limited. Easy, huh? Sure, this won't work well if you're reading this, say, twenty years from the death of the universe (whatever that means) and something is still steadily improving, but then one, you have more important things to do than reading this, and two, I'd be really proud that someone so (hopefully) far in the future bothers at all to read what I wrote.

Usually, if something improves over your lifetime it's nice, if it degrades it's sad but that's just how things go, but people really don't like it if something improves and then degrades. The point is, if everything is eventually going to shit then there must be some unlucky people living when a thing peaks in "goodness". Given that lots of things exist, it is a natural consequence that lots of things start getting worse during a person's lifetime. (Somewhat like xkcd 1053; I misremembered the number as 1054, by the way.) It's great when we see things improve and even better when we work to make things improve; the point is that it really isn't anyone's fault that a thing stops improving and starts getting worse: it is inevitably going away anyway.

If one pictures a "goodness" curve that goes up, reaches its peak, then goes down, it's obvious that for a given thing yet to reach its peak, the better it is at an instant, the closer that instant is to when the thing start getting worse. This is, of course, different from saying that the better a thing is, the closer it is from starting to get worse: things don't have a predetermined "time to peak"; however the latter is in a sense true. From a utilitarian view, to maximize total goodness over time before the peak, one either increases goodness without shortening time to peak, or reduces slope without reducing peak goodness. Thus, an improvement is not necessarily "good" if it makes the thing closer to peak.

2026-06-26

More playing with my new printer... It makes some interesting noise when printing evenly spaced horizontal lines, but not good enough for music. Another experiment was to put in a segment of paper roll, cellotape its ends to create a loop, and then make the printer feed paper. The loop eventually breaks because of accumulated errors in its horizontal position. I also tried the Möbius strip, but it breaks much faster, as one should expect.

Some AWK that prints 50-column text in a small 8x16 font:

{
    gsub("\"", "\\[\"]")
    printf "TEXT 0,%d,\"1\",0,1,1,\"%s\"\n", ((NR-1)%40)*18, $0
}
BEGIN { print "SIZE 50 mm,90 mm"; print "CLS" }
NR % 40 == 0 { print "PRINT 1"; print "CLS" }
END {
    if (NR % 40) {
        printf "SIZE 50 mm,%d mm\n", (((NR%40)*18)+7)/8
        print "PRINT 1"; print "CLS"
    }
    print "FEED 200"
}

2026-06-25

I set up this website a few weeks ago with Caddy running on Debian, using automatic HTTPS. Later, I tried doing the same on another machine running Arch, but it's behind a firewall that blocks port 80 and thus breaks Let's Encrypt ACME. Just now I randomly found a valid certificate (that is, from a random place, not by brute force searching for a valid signature), and ran Caddy on the new machine.

But it wouldn't start, saying it doesn't have permission to open the TLS certificate. I checked the PEM files' permissions, which are correct. Then I realized that I should also check the systemd unit file. Turns out, it says, under "Hardening options":

ReadWritePaths=/var/lib/caddy /var/log/caddy /run/caddy

And, after installing the package, I moved the automatically created Caddy user's home directory to /home/caddy because I did that on the Debian machine since it's shorter (easier to type) and doesn't need quoting. I ended up moving back to /var/lib/caddy, the "proper" place, on both machines.

More on quoting: in an scp command one may refer to the home directory of the Caddy user on the remote machine with remote:~caddy, unless the local machine has a Caddy user with a different home directory, since the shell doesn't expand ~caddy if the local machine doesn't have a Caddy user and the expansion happens to be correct if both machines have Caddy users with identical home directories. Looks to me like a footgun, so I always quote it.

At least Arch's Caddy is new enough to have sane defaults for the encode directive; on Debian I needed encode gzip zstd to do the same thing.

Unrelated trivia: explain

$ ssh foo sh -c 'echo foo; echo bar' 

bar

2026-06-21

Got a second-hand HPRT N31 thermal printer, which prints 72 mm wide at 8 dots per mm (~200 dpi). My old thermal printer, an Xprinter 58IIH, only prints 48 mm wide, so it's a nice upgrade. Both offer a USB Type-B port and appear as /dev/usb/lp0. The new printer offers a line-oriented command language called TSPL, for which I found a programming manual by HPRT online but it doesn't claim to apply to my model. It certainly didn't help that the indicator light flashes green even if the command is invalid. I eventually figured out that the basic commands do work, that I need GAP 0,0 to make it work with paper rolls, and that double quotes in strings are escaped as \["], so anyway, but I certainly prefer ESC/POS and Xprinter's manual for it.

The new printer can backfeed, which I wanted to combine with variable density to print grayscale, but backfeed is so imprecise I couldn't even double strike text, so that's a dead end.

I opened up the machine hoping to find a SOIC-8 flash chip I can dump, but instead found a big-ass QFP-100 MCU with a sticker over its top, which I guess has the flash built in. I didn't have the patience to carefully remove that sticker so I gave up. I did see an antenna in the PCB and a bunch of empty solder pads nearby, presumably for variants with Bluetooth and/or Wi-Fi interfaces. (I didn't open the Xprinter since it takes 220 V AC; the new one takes 24 V DC from a separate power adapter.)

Here's some bash that prints a PBM image (the printer treats zero bits as black, so if the width is not a multiple of 8, the zero padding bits at the end of each row become a black vertical bar):

magick logo: -resize 576x -dither FloydSteinberg -colors 2 -negate pbm:- | {
    read; read w h
    echo 'CLS'
    printf 'SIZE %d mm,%d mm\n' $(((w+7)/8)) $(((h+7)/8))  # for paper rolls
    printf 'BITMAP 0,0,%d,%d,0,' $(((w+7)/8)) $h
    cat
    echo 'PRINT 1'  # or any number of copies
} > /dev/usb/lp0

And this is for the Xprinter. The loop is because the printer cannot handle high images (I ran into this problem trying to print text banners like "KEEP OUT KEEP OUT KEEP OUT"); the HPRT has a similar limit but I haven't hit it yet:

le16() { printf $(printf '\\%o\\%o' $(($1&255)) $(($1>>8))); }

magick -font Comic-Sans-MS -pointsize 400 label:'DUMB SHIT' \
    -gravity center -extent x384 -rotate 90 pbm:- | {
    read; read w h
    for (( ; h > 2000; h -= 2000 )) do
        printf '\x1d\x76\x30\x30'
        le16 $(((w+7)/8)); le16 2000
        head -c $((w*2000))
    done
    printf '\x1d\x76\x30\x30'
    le16 $(((w+7)/8)); le16 $h
    cat
} > /dev/usb/lp0

2026-06-11

Just figured out how to use a crappy computer I bought earlier with a pathetic N3150 processor and 4 GB of RAM, without attaching a monitor (which is a huge hassle): SSH. Just boot an Archlinux install ISO on it, attach it to my laptop via Ethernet, attach a keyboard, wait until the Ethernet lights start blinking, and type (very carefully):

a=`echo /sys/class/net/e*`
a=${a#/sys/class/net/}
ip a a 192.168.0.2/30 dev $a
passwd
secret
secret

And, on my laptop:

ip address add 192.168.0.1/30 dev eth0
ssh -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    -o ObscureKeystrokeTiming=no \
    root@192.168.0.2

I came up with the trick to get the name of the Ethernet interface, the first two options to ssh keep the known_hosts file clean (see ArchWiki), and the third one makes the light blink exactly once per key stroke, which is nice.

Having to attach a keyboard is still annoying, and I can't persist anything since it doesn't have a disk and writing to the thumb drive is silly. (Update 2026-06-21: it's now running with an old crappy disk.)

2026-06-08

Yesterday I learned that JIS B5 is a different paper size than ISO B5.

2026-05-27

Just watched Hypernormalisation and it's quite good. Familiar ideas from a somewhat different perspective, which is always refreshing. One thing that bugs me though is attributing suicide bombing to the Middle East: one Chinese soldier did it to slow down the Japs (didn't kill any though; this is also disputed, but his comrades support the story and the authority claims more), and the Japs did it to the Americans (Kamikaze).

I tried the sleeping pill (that I have) and it seems to work, except it makes me sleepy when awake as well. I guess I'll live with that till I get to see a doctor. (I'm prescribing myself: a dead man can't see a doctor.)

2026-05-21

I'm having yet more sleep problems and I hate this. Maybe I should try and entertain myself some more: anime, music, movies, books for fun... I guess I won't get any work done anyway, even if I try all the time, so having some fun is the rational choice. I'll try taking sleep pills and see if that helps.

It's funny how easy updating this website is. I'm so lazy that organizing changes into git commits seems like work, and I don't use a static site generator so I don't benefit from CI. Updating this website is just rsync -r www/now.vursc.org vps:now.vursc.org.

I'm using the deprecated (or whatever people call that these days) <tt> tag here, and I really don't understand why the HTML people decided to remove it. Not everything monospace is code, and <b>, <i>, <u>, and <s> are kept anyway, so what is the point of removing <tt>?

2026-05-17

I'm thinking about a new "generic" text markup language. Markdown is not expressive enough and suffers from incompatibility. reStructuredText is tied to Python and has "weird" syntax rules like the double colon and whitespace around inline markup, which makes the format more intuitive at the expense of complexity. (La)TeX is too low-level and, though not inherent, mostly tied to paper.

I think I will build it in JavaScript first, since I need KaTeX. The dollar sign is too hard to type (I need to press shift), let alone the LaTeX-style \( and \[. The backtick is better: just one keystroke and not used much in math. Backticks are still useful for inline code, so the choice will be a document-wide option (or directive). Now that I have two kinds of backtick-delimited "raw" content, I might as well make the starting backtick take a type tag, so m`E=mc^2` is explicit math, c`foo(bar())` is explicit code, and if the starting backtick follows a space or newline the default type is used and the space is preserved.

Escaping is a problem. It wouldn't be if everyone just writes s-exprs, but real people are lazy. The easiest way is to just disallow the backtick in backtick-delimited content. Another delimiter is needed for markup containing literal backticks, and I choose brackets since they are also easy to type and not used much in prose. The escape character is the conventional backslash. Thus m[E=mc^2] is exactly the same as m`E=mc^2`. Brackets can nest into a tree, and it would be nice if the parser can be generic, so the syntax is mostly uniform, like s-exprs. (Todo: alternative designs for escaping and their problems; markup with multiple arguments.)

Paragraphs are so common they deserve special syntax, though. Paragraphs are usually delimited by empty lines, but for CJK languages, which doesn't separate words with spaces, either conversion to HTML or the browser has to decide if a space should be inserted when joining two lines, which is ugly. So one long line per paragraph is the way to go. I keep the empty lines in between because wrapped long lines are hard to tell apart.

A curiosity is to embed s-exprs in this markup. One may simply wrap each atom in brackets, [[like] [this]], or use backticks if the atom doesn't contain one, [`like` `this`]. It would be nice if simple words doesn't need quoting, but then one cannot tell if [foo] is the atom foo or the list (foo).


Legal notice: You must not access this website if you are a citizen, resident, or otherwise legally related to China, the US, the UK, Australia, or any member state of the EU.