koldfront

open-qr-code #shell

🕘︎ - 2025-06-10

It's not that often I encounter a QR code I want to open on my computer, but when I do it would be nice to simply be able to select the graphic on the screen, and then have the URL open.

Combining Flameshot and zbar-tools in a small shell script allows me to do just that: the script checks for PNG in the clipboard, and if there is none, launches Flameshot. After that zbarimg is run to extract the URL, which is then opened in the default browser:

#!/bin/sh

# open-qr-code - script to interpret a qr-code in the clipboard and
#                open it.
#
#     Depends on: xclip, zbar-tools, libnotify-bin, flameshot.

# 1) Save png from clipboard in file:
CAPTURE=0
if ! xclip -o -t TARGETS -selection clipboard | grep --quiet image/png; then
    # 0) If there is no image in clipboard, launch flameshot to pick one:
    sed -i 's/showDesktopNotification=true/showDesktopNotification=false/' ~/.config/flameshot/flameshot.ini
    flameshot gui --clipboard --accept-on-select
    sed -i 's/showDesktopNotification=false/showDesktopNotification=true/' ~/.config/flameshot/flameshot.ini
    CAPTURE=1
fi
if xclip -o -t TARGETS -selection clipboard | grep --quiet image/png; then
    IMAGE=$(/bin/mktemp --tmpdir qr-codeXXXXXX.png)
    xclip -t image/png -selection clipboard -o > "$IMAGE"
    # 2) Run QR-code detector:
    if CONTENT=$(zbarimg --quiet "$IMAGE"); then
        # 3) If it is a URL, open it in a browser:
        URL=$(echo "$CONTENT" | sed 's/^QR-Code://')
        if echo "$URL" | grep --quiet '^http'; then
            notify-send --icon /usr/share/icons/Moka/256x256@2x/apps/qr-creator.png "Open QR code" "Opened $URL"
            x-www-browser --new-window "$URL" &
        else
            notify-send --icon /usr/share/icons/Moka/256x256@2x/apps/qr-creator.png "Open QR code" "QR code contained non-url: '$URL'"
        fi
    else
        notify-send --icon /usr/share/icons/Moka/256x256@2x/apps/qr-creator.png "Open QR code" "Did not find a QR code in the image"
    fi
    if [ $CAPTURE = 1 ]; then
        echo "" | xclip -in -selection clipboard
    fi
else
    notify-send --icon /usr/share/icons/Moka/256x256@2x/apps/qr-creator.png "Open QR code" "No image/png in clipboard"
fi

watchfile #shell

🕘︎ - 2025-06-09

Recently I was editing a Markdown file and I was converting it to HTML with pandoc for every edit.

Re-running the command quickly became tedious - I could use some kind of framework for editing Markdown or something elaborate, but what I really wanted was just "run a command when a file changes".

So, watchfile was born - it basically just wraps inotifywait from the inotify-tools package:

#!/bin/sh

# watchfile FILE COMMAND - run a command whenever a file changes.

FILE=$1
shift

while inotifywait --quiet --quiet "$FILE"; do
    echo -n "Running '$*' ... "
    "$@"
    echo "done."
done

dl #shell

🕣︎ - 2025-06-08

One of the small, handy shell scripts I use all the time is called dl. If called with arguments, URLs, it will try downloading each argument using yt-dlp.

That's pretty mundane, basically just easier to spell. But here is the twist: if called without arguments, dl examines the clipboard, looking for a URL, and downloads it if found, then the selection.

So often I will see a link to a video, copy the address and then open a shell, type dl and away we go - skipping the "paste the URl"-step.

Small improvement, but very handy.

P.S. It also retries.

#!/bin/bash

# dl - wrapper to download with yt-dlp from clipboard to ~/video.
#
# Copyright (C) 2020-2025, by Adam Sjøgren  Under the GPLv2.

set -o pipefail

cd ~/video || exit 10

MAX_RETRIES=5

ARGS=( "$@" )
if [ "$*" == "" ]; then
    ARGS[0]=$(xsel --output --clipboard)
    if [[ ${ARGS[0]} != http* ]]; then
        # If not the clipboard, maybe the mouse:
        ARGS[0]=$(xsel --output)
        echo "dl: clipboard is empty, using string from the mouse"
    else
        # Clear clipboard if we got a URL from it, so we are ready for
        # the next, possibly from the mouse:
        echo "dl: using string from the clipboard"
        # This doesn't work, at least not with Emacs:
        #   xsel --clear --clipboard
        # so do this instead:
        echo -n "" | xsel --input --clipboard
    fi
fi

i=0
for url in "${ARGS[@]}"; do
    if [[ "$url" != http* ]]; then
        echo "'$url' doesn't look like a URL"
        exit 10
    fi
    url=$(echo "$url" | sed 's/drtv\/episode\//drtv\/se\//')
    url=$(echo "$url" | sed 's/drtv\/program\//drtv\/se\//')
    if [ $i -gt 0 ]; then
        echo ""
    fi
    FILE=$url
    LAST_ERROR=1
    TRY=1
    echo "Downloading $url ..."
    while ((LAST_ERROR != 0 && TRY < MAX_RETRIES)); do
        if [ $TRY -gt 1 ]; then
            echo "Trying again ($LAST_ERROR, $TRY)"
        fi
        yt-dlp --force-ipv4 --color always --print 'before_dl:%(title)s' --print-to-file before_dl:title "/tmp/dl-out-$$.txt" "$url"
        LAST_ERROR=$?
        FILE=$(cat /tmp/dl-out-$$.txt)
        rm /tmp/dl-out-$$.txt
        TRY=$((TRY + 1))
    done
    if [ $LAST_ERROR != 0 ]; then
        echo "Failed dl $url"
    else
        TITLE=$(ffprobe "$FILE" 2>&1 | grep ' title ' | cut -d: -f2-)
        if [ "$TITLE" == "" ]; then
            TITLE="$FILE"
        fi
        if [ "$TITLE" == "" ]; then
            TITLE="$url"
        fi
        notify-send --icon /usr/share/pixmaps/terminal-tango.svg --expire-time 2000 'dl' "${TITLE} downloaded"
        echo "Done."
    fi
    i=$((i+1))
done

Mark V. Shaney's descendants #usenet #tuhs

🕒︎ - 2025-05-29

A couple of days ago, LLM generated text was discussed on The Unix Heritage Society mailing list, and this exchange caught my eye:

an LLM is pretty much just a much-fancier and better-automated descendant of Mark V Shaney: https://en.wikipedia.org/wiki/Mark_V._Shaney

I am glad someone has finally pointed that out.

I followed the Wikipedia link and learned that "Mark V. Shaney" was the name used to post markov chain generated usenet articles, made at Bell Labs back in the 1980's.

I wasn't on usenet in 1984, but I do have an archive, so let me look up the Mark V. Shaney articles I can find:

Message-ID Date Newsgroup Subject
2980@alice.UUCP 1984-09-12 net.singles Change of topic?
2990@alice.UUCP 1984-09-18 net.singles RE: Re: RE: Re: External Appearances
2991@alice.UUCP 1984-09-19 net.singles Re: IBM software
2992@alice.UUCP 1984-09-20 net.singles Re: data processing
2998@alice.UUCP 1984-09-22 net.singles Don't call me a machine!!!
3010@alice.UUCP 1984-09-27 net.singles Re: sensitivity
3014@alice.UUCP 1984-09-29 net.singles Re: tease
3022@alice.UUCP 1984-10-02 net.singles Re: Money for your musings
3025@alice.UUCP 1984-10-04 net.singles Re: censorship
3031@alice.UUCP 1984-10-06 net.singles Re: censorship
3037@alice.UUCP 1984-10-12 net.singles Subject: Re: What is sensitivity good for anyway?
3043@alice.UUCP 1984-10-15 net.singles I'm a hugger, I'm a tactile programmer
3044@alice.UUCP 1984-10-16 net.singles Subject: Do not meddle in the mouth.
3048@alice.UUCP 1984-10-18 net.singles Subject: Re: mod.singles is now activated
3050@alice.UUCP 1984-10-19 net.singles Re: Not Way-Out But Lovable Babbling Meaningless Drivel [ :-( ]
3053@alice.UUCP 1984-10-22 net.singles Re: Kate Hepburn on sex life of today's college students
3058@alice.UUCP 1984-10-24 net.singles Re: Te: Re: backlash to the feminist movement
3059@alice.UUCP 1984-10-25 net.singles Re: How to ask for a phone number
3062@alice.UUCP 1984-10-27 net.singles Re: Re: Advertising with bikini-bait
3075@alice.UUCP 1984-11-05 net.singles Re: Gorilla my dreams
3079@alice.UUCP 1984-11-06 net.singles the definitive party followup!
3112@alice.UUCP 1984-11-16 net.singles Party Politics (follow-up)
3125@alice.UUCP 1984-11-30 net.singles Re: parental approval of, umm, living arrangements
3132@alice.UUCP 1984-12-01 net.singles Re: unconditional love
3133@alice.UUCP 1984-12-01 net.singles Re: unconditional love
3215@alice.UUCP 1984-12-29 net.singles Re: Big Breasts: The Unresolved Trauma
3216@alice.UUCP 1984-12-29 net.singles Re: A Seasonal Note
3862@alice.UUCP 1985-06-16 net.singles Friendship before/after SOship
3866@alice.UUCP 1985-06-18 net.singles Re: Most Bitter Attack on A Good Man et al.
3872@alice.UUCP 1985-06-19 net.singles Re: Intelligence (mild flame)
3889@alice.UUCP 1985-06-23 net.singles Re: Most Bitter Attack on A Good Man
3902@alice.UUCP 1985-06-26 net.singles Most Bitter Attack on A Good Man
3920@alice.UUCP 1985-06-28 net.med Re: Hayfever and Raw Honey
3952@alice.UUCP 1985-07-02 net.singles Re: The Good Old Times
4107@alice.UUCP 1985-08-04 net.mail Re: Mail routing -- problems showing up

The Wikipedia entry has this information, under Examples:

Other quotations from Mark's Usenet posts are:[3]

  • "I spent an interesting evening recently with a grain of salt." (Alternatively reported as "While at a conference a few weeks back, I spent an interesting evening with a grain of salt."[4][5])
  • "I hope that there are sour apples in every bushel."[6][7] (see also sour grapes)

Interestingly we can solve the "alternatively reported" issue by going to the source, which is the very first article in the archive, 2980@alice.UUCP, where it says:

When I meet someone on a professional basis, I want them to shave their arms. While at a conference a few weeks back, I spent an interesting evening with a grain of salt. I wouldn't take them seriously! This brings me back to the brash people who dare others to do so or not. I love a good flame argument, probably more than anyone...

(my emphasis).

The second, sour apples quote from the Wikipedia entry is from the article 3062@alice.UUCP, where it is the closing line.

The (almost) full article shown in the Wikipedia Examples section is 3112@alice.UUCP; it looks like Wikipedia is missing the quip below the sign off, "Never attribute to malice what can be found in scientific american, under computer recreations."

The program behind Mark V. Shaney used a third order markov chain, so it makes statistics on triplets of words, and generates the next work by looking two words back. Reading the usenet articles, I want to revisit my little "politisnak" project, which is a basic markov chain looking at tuples of words, and expand it.

Sometimes it's fun to sit on top of a trove of old usenet articles!

Rob Pike added to the exchange on the TUHS mailing list, quoted at the beginning:

My name is Rob Pike and I approve this message.

:-)

Debian 13 (trixie) #debian #gnupg

🕛︎ - 2025-05-17

A week or so ago I upgraded my laptop to the upcoming Debian 13 (trixie). I was hoping that it would fix VLC not showing any pictures for some videos (it didn't help, the fix was to change "Hardware-accelerated decoding" from "VDPAU video decoder" to "VA-API video decoder").

The only problem I have had so far is GnuPG, which has changed behaviour, so now it tries all private keys when decrypting eg my .authinfo.gpg - prompting for cards and what have you.

It also broke the integration with Emacs, which is annoying as my password manager is simply a gpg-encrypted text file.

Downgrading to the gnupg package in Debian 12 (bookworm) helped, so that's my solution so far.

Bridging the fediverse #activitypub #fediverse #illuminant

🕤︎ - 2025-05-16
Illuminant - a fediverse server for those who prefer NNTP. Yes, NNTP.

A couple of years ago I started making an ActivityPub server to make it easy for me to access the fediverse over nntp.

One of the challenges - at least in the way I built my server - is that every server it talks to speaks a slightly different dialect of ActivityPub. Mastodon generates JSON with one structure, Pleroma might include a field that others don't, GotoSocial might leave one out, etc. etc.

Around a month ago Klaus lured me into trying the Bluesky↔Fediverse bridge Bridgy Fed. TechCrunch had a nice introduction on how to bridge your account, which I followed.

And then I had to adjust my ActivityPub server to the quirks of the bridge. Usually it's stuff like "this field I have only ever encountered having a string as the value, but now it's a dictionary, or a list", and it takes me a while to handle all of them.

Had I implemented the spec rather than just building something and trying it against other servers, I would probably have fewer of these surprises. But, hey, I was (and still am) building Illuminant for fun, so I was happy to implement just enough to interact.

One thing that in hindsight is sort of expected and also sort of a downer is that boosts and parent blueskidoos only show up if the sender's account is bridged. So rather than being a useful expanse of the fediverse, this bridging turns out - to me, anyway - to be more of a curiosity than anything else.

Saving and restoring an Emacs macro #emacs

🕧︎ - 2025-04-18
Emacs logo

From time to time I add domains to a configuration file, header_checks, for my mail server, Postfix, to block them as spam.

To do this, I copy the email-address from a spam email, paste it into the configuration file, and modify it to escape the . and remove up until the @.

Doing that for a lot of spam is of course tedious, and so I usually create a macro in Emacs to do the pasting and massaging.

However, creating that macro is a bit tedious and that makes me not do it for the first couple of spams. Until I get too annoyed. So it keeps me from updating the checks. Not great.

Emacs is capable of generating the lisp code corresponding to a macro, so I thought "Hah, I will save the macro in a comment in the file, then I can evaluate it when I'm updating the file, and I don't have to record the macro every time!"

Alas, the command M-x insert-kdb-macro RET inserted some code that gave an error when evaluated - how odd?!

At first I assumed I was doing something wrong - the documentation only mentions saving a named macro and I was saving the last, unnamed one - but after looking at the lisp code of the function insert-kbd-macro (Emacs makes this easy), it was clear to me that it was supposed to work: it contains specific code to handle the last macro case.

I also tried an earlier version of Emacs (I usually run a development version), the one in Debian stable - version 28.2, and there the code generated was different, and, even more interestingly, worked!

So, I created a bug report for Emacs (#77317) and was swiftly told by one of the current Emacs maintainers that the documentation only says it works for named macros.

I argued that the function specifically has a branch for the last (and unnamed) keyboard macro, and that the functionality was broken in 2022, and Cc'ed the previous Emacs maintainer who made the breaking change (which was an improvement to the other branch of the function).

He asked for an explanation of my use case, and after I supplied that, he made a change to fix the problem, which I tested swiftly; it worked, and pushed it to the development version of Emacs a couple of days later.

Nice!

So, now I could easily generate the code to restore the macro and put it in a comment in the file.

Shortly after it hit me: Emacs can evaluate code from a file when loading it, I could have it restore the macro automatically on opening the file!

A quick 3 lines at the bottom of the file later:

# Local Variables:
# eval: (setq last-kbd-macro (kmacro--keys (kmacro "| C-y M-<left> <left> [ <right> ] C-r @ <right> M-<backspace> C-s ) <left>")))
# End:

and now, more than ever, Bob is indeed my uncle!

Lille langebro

Today

Che Guevara (97).

UNIVAC I (74).

Storebælt (27).

Tomorrow

'Dannebrog' fell from the sky (806).

Monday

Bloomsday (121).

Valentina Teresjkova first woman in space, Vostok 6 (62).

Day of the African Child (34).

APoD (30).

Adam: Installed Yggdrasil Fall 1994 - first GNU/Linux distribution (31).

Tuesday

Iceland independent (81).

Wednesday

Brother (48).

Mihtjel (42).

Poul Nesgaard (73).

Thursday

Aung San Suu Kyi (80).

Jeremy Sajjabi (30).

Paul McCartney (83).

Friday

Errol Flynn (116).

2025-06-21

Masaharu Goto (62).

Pernille Seier (57).

Greenland selfgovernment (46).

MamaTux (45).

Erfalasorput – Flag of Greenland (40).

OFTC (23).

International Day of Yoga (11).

2025-06-23

Alan Turing (113).

LGBT+ Danmark (77).

DNS (42).

2025-06-24

DK: Skt. Hans (birthday of St. John the Baptist).

2025-06-27

Langebro (71).

2025-06-28

Atari Inc. (53).

τ day.

2025-06-29

GNU GPL v3 (18).