xdg-override: change default application temporarily on Linux
github.com
github.com
This is not that different from "xdg-override"; you can add global overrides for specific URLs with a simple match, and matches from a specific application by checking the parent process (e.g. $(readlink /proc/$PPID/exe) = /usr/bin/slack, or some such). It seems easier to me to have everything in one place.
I also considered popping up dmenu for unknown filetypes, but I don't use it that much and don't really need it.
At any rate, "to whom it might be useful":
#!/bin/zsh
#
# xdg-open, without suckage.
[ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1
setopt err_exit no_unset pipefail
(( $+1 )) || { print >&2 'need a parameter'; exit 1 }
# Open URL.
if [[ -z "${1:#*://*}" ]]; then
proto=${(L)1%%://*}
case $proto in
(http|https) exec firefox $1 ;;
(file)
1=${1##$proto://} # Remove protocol
1=/${1#*/} # Remove hostname
;;
(*)
print >&2 "xdg-open: no URL handler for protocol '$proto://' (for: $1)"
exit 1
esac
fi
# Open file.
ext=${(L)1:e}
case $ext in
(html|htm|pdf) exec firefox $1 ;;
(png|webp|jpg|jpeg|heic) exec firefox $1 ;;
(mp?|ogg|flac|m4v|mkv|avi|mov|wav) exec mpv $1 ;;
(md|markdown|txt|sh|zsh|go|py|rb|js|c|json|xml) exec st -e vim $1 ;;
(*)
mime=$(file -b --mime $1 2>&/dev/null ||:)
t=${mime%/*}
case $t in
(text) exec st -e vim $1 ;;
(image) exec firefox $1 ;;
(audio|video) exec mpv $1 ;;
esac
print >&2 "xdg-open: don't know how to open '.$ext' files (mime: '$mime') (for: $1)"
exit 1
esacThe complexity comes from the integration with various desktop environments. And this I understand. Ideally, everyone would use xdg-utils, but there's legacy, backward compatibility, what not.
What I don't understand is why those DE-specific openers do not always respect xdg settings. For instance, kde-open would respect mineapps.list for almost everything except default browser.
For me this is the main source of confusion and I would guess, this is the main driver behind numerous xdg-open alternatives out there.
Plus, XDG is the de facto standard. Its implementation so far is not perfect, but I feel like aligning with the standard is better than diverging from it.
Pretty much, yeah. It's just a simple mapping, right? I don't recall what problems I had exactly, but it was just a pain and stopped working at various points, or just didn't do what I wanted and couldn't get to behave as I wanted. So I spent half an hour six or seven years ago and that fixed it (I added the MIME support last year or so, another 15 minutes).
At some point of knowledge it's easier and less time-consuming to just script these things rather than relying on some generic system. It is for me anyway.
Usually I'm the first one who defends open source solutions and blames complexity and compatibility... But xdg-open is just an awful interface to interactive with, probably my most hated Linux desktop cli tool.
1. it can be called with just a single argument 2. if it's running under a DE, it delegates to the ${de}-open. In the same fashion, it handles some esoteric cases, like flatpak and WSL2... 3. if it's not running under a DE or it's one of the esoteric cases, it parses $1 and reads associations from an INI file 4. finally if the executable is supplied in a form of desktop file, it resolves it to a command
Most of its alternatives do steps 1 and 3, and do not do steps 2 and 4. And it doesn't seem like too much incidental complexity tbh. Step 4 seems totally legit. Step 2 — controversial, but I can rationalize this with legacy and backward compatibility.
And even when we start thinking about all those ${de}-open... Probably those are needed for some eye candy, like jumping icons in the dock when an app is launching... IDK, I'm just speculating.
Looking through the source code briefly, it seems like it's almost a full re-implementation of `xdg-open`. My solution is in a different league. I didn't mean to replace `xdg-open` and aimed to augment `xdg-open` while being as simple as possible.
https://github.com/vincentbernat/i3wm-configuration/blob/mas...
JK, i3 user here as well.
The logic of xdg-open is straightforward. But once it's invoked from within a major DE, its behavior is sometimes not so obvious as it tries to delegate to a bunch of other tools.
but sadly, many projects, programms which I came across (mostly but not exclusively daemons), defines PATH for themself, not respecting inheritence: saying "what if someone set it up maliciously" or "default PATH is not enough / too wide / in wrong order / etc" ...
man, sorry, process! what you inherit is what there is, it's your environment where you need to do your job. you can't? just fail and let the caller know the error case. will you (as an app author) change the CWD because it's unfamiliar (breaking relative path parameters along the way)? or redirect STDERR from a file to tty because "user MUST SEE this error" (making it NOT shown in the error log where the user did want to put the error output)? no. that's why you should not touch PATH. except if you are a logon process, a wrapper whose responsibility is to prepare the env for descendents, or crossing privilege boundary.
due to this defiance of many programms, I started to switch from PATH-prepending to "over-mount executables in separate mount namespace" strategy. I hope it won't be voided by they invent "let's switch to the default namespace because it's my will" type of overreach.
Oh! YESSSSS!!! FINALLY!!! I've been removing x permissions for xdg-open for YEARS!!! I hope this works.
xdg-override is not a replacement for xdg-open. It's a script, that selectively replaces xdg-open in narrow explicitly defined cases.
But some xdg-open alternatives have been mentioned in the comments.
It's a simple python script that checks by looking at the protocol, mime type or extension which URL it is and also asks if there are multiple applications
https://paste.sr.ht/~fossdd/7fa65e10998ebcc03a2bbcc8488f94e9... (CC0)
Like jojo14 points out Python has the shlex module, and it is definitely useful in these situations even if just for quote(). And, os.system() is basically never safe with external input. Without proper escaping you're one click from code execution, for example with the input "file:///etc/issue%3Becho%20whoops" or "http://example.com/';echo whoops'".
It doesn't appear to matter in this instance, but you can feed check_output() with the stdin argument, which removes the need for using shell=True. shell=True in other paths could easily lead to unwanted code execution without thorough escaping.
#!/bin/sh
if [ "$@" = "/tmp/gpauth.html" ]
then
flatpak run --filesystem=/tmp/gpauth.html org.chromium.Chromium /tmp/gpauth.html
else
/usr/bin/xdg-open "$@"
fixdg-override temporarily and selectively replaces the application opened by default (without the "open with" menu and in places where this menu isn't even available)
or just
application file.ext
Some people just really take any excuse to write a program.
I can't change Slack source code to invoke `firefox $url` for me instead of xdg-open it currently invokes.
If you know a way, please share.
'firefox url'
directly on the terminal?
To be clear, the override script is closer to going ...
mkdir ~/custombin/
ln $(which firefox) ~/custombin/xdg-open
PATH=~/custombin:$PATH slack
... than going `firefox url`