---
title: "Open URLs based on regular expressions"
slug: "regex-based-url-opener"
description: null
date: 2022-04-18T17:16:08+02:00
type: posts
draft: true
tags:
- WWW
- Zsh
toc: true
---

:source-highlighter: pygments
:idprefix:
:experimental: true
:toc:
:toclevels: 2

:url-urlhandler: https://schlomp.space/tastytea/dotfiles/src/branch/main/.local/bin/urlhandler
:url-functions: https://schlomp.space/tastytea/dotfiles/src/branch/main/.config/zsh/functions
:url-zshoptions: https://zsh.sourceforge.io/Doc/Release/Options.html
:url-zshfunctions: https://zsh.sourceforge.io/Doc/Release/Functions.html
:url-desktop-spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/

:abbr-pcre: pass:[<abbr title='Perl Compatible Regular Expressions'>PCRE</abbr>]

One of the shell scripts that bring me the most joy is my URL opener. I have it
set as my default browser, so whenever I click a hyperlink somewhere it gets
called and decides if the URL should be opened with Firefox or with another
program. mpv for video files and YouTube URLs, a download-and-launch-viewer
function for pictures and so on.

== The script

I have abbreviated the script here a bit for better readability, the full
version is available link:{url-urlhandler}[in my dotfiles repo].

[source,shell]
--------------------------------------------------------------------------------
#!/usr/bin/env zsh
# Open URLs based on perl compatible regular expressions. Several commands can
# be specified with ; as separator. If the URL doesn't match anything or the
# commands are not found, use ${default_cmd}.

setopt ERR_RETURN NO_UNSET PIPE_FAIL

if [[ ! -v 1 ]]; then
    print -u 2 "usage: ${0} <URL> …"
    return 1
fi
local -a urls=(${@})

autoload -U readwwwlog openwwwimg
zmodload zsh/pcre

local default_cmd="firefox"
--------------------------------------------------------------------------------

First, we set some essential options. `ERR_RETURN` stops execution when a
command has a non-zero exit status, `NO_UNSET` treats non-existing variables as
an error and `PIPE_FAIL` sets `${?}` to non-zero if any command in a pipeline
fails. You can read more about them in the *zshoptions* man-page or in the
link:{url-zshoptions}[online documentation].

Next we check if `${1}` is set (we have at least 1 URL) and assign all arguments
to the array `${urls}`. Then we autoload some function link:#helper_functions[we
later use], load the {abbr-pcre} module and set the default browser.

[source,shell]
--------------------------------------------------------------------------------
local mpv="mpv --force-window=yes"

local -A assignments=(
    '\.(mp4|m4v|mkv|avi|webm|flv|xvid|ogv|theora|mov|wmv)$' "${mpv};vlc"
    '\.(ogg|flac|opus|m4a|wav|mp3|mid|aac|wma)$'            "${mpv};vlc"
    '^https://media\.ccc\.de/v/[^/]*$'                      "${mpv}"
    '^https?://(www\.)?youtu(\.be|be\.com)/'                "${mpv}"
    '^(gemini|gopher)://'                                   "kristall"
    '\.(log|txt)$'                                          "readwwwlog"
    '^https?://(www\.)?(dpaste|pastebin|irccloud)\.com/'    "readwwwlog"
    '\.(webm|png|jpe?g|gif|svg|bmp|xcf)$'                   "openwwwimg"
)
--------------------------------------------------------------------------------

Now we define the associative array `${assignments}` with the regular expression
on the left and the command(s) to run on the right. The first command that
exists will be run.

[source,shell]
--------------------------------------------------------------------------------
local selected_cmd=${default_cmd}
for regex cmds in ${(kv)assignments}; do
    if [[ ${urls[1]} -pcre-match ${regex} ]]; then
        for cmd in ${(s/;/)cmds}; do
            if type ${cmd%% *} >& -; then
                selected_cmd=${cmd}
                break
            fi
        done
        break
    fi
done

${(@s/ /)selected_cmd} ${urls}
--------------------------------------------------------------------------------

This loops through all regular expressions and compares it against the
first URL (we naively assume that all supplied URLs are of the same type 😊). If
it matches, the first command that exists is run with the URL. If nothing
matches, the default browser we set earlier is used.

[NOTE]
Because commands can contain options, we split the command-strings at spaces in
the existence check and when we run the command. This has the side-effect that
commands can not contain spaces.

== Helper functions

The functions `readwwwlog` and `openwwwimage` are available in the
link:{url-functions}[Zsh functions directory] of my dotfiles repo. Put them
anywhere in your `${fpath}`. Learn more from the *zshmisc* man-page or the
link:{url-zshfunctions}[online documentation]. Or you could just put them inside
the script, like this for example:

[source,shell]
--------------------------------------------------------------------------------
function openwwwimg() {
    local file="$(mktemp --suffix='.openwwwimg')"
    curl --silent --location --output ${file} ${url}
    if [[ ${?} -eq 0 ]]; then
        viewnior ${file}
    else
        firefox ${url}
    fi
    rm -f ${file}
}
--------------------------------------------------------------------------------

Don't forget to remove the autoloads if you put them directly in the script.

== Setting the script as default browser

To set the default browser in Linux, you need to have a `.desktop` file
(link:{url-desktop-spec}[specification]).

[source,toml]
--------------------------------------------------------------------------------
[Desktop Entry]
Type=Application
Version=1.0
Name=urlhandler
Comment=Open URLs with different commands
TryExec=urlhandler
Exec=urlhandler %u
Terminal=false
--------------------------------------------------------------------------------

Save this in `~/.local/share/applications/urlhandler.desktop` and run
`xdg-settings set default-web-browser urlhandler.desktop`. Congratulations, you
are ready to go! 🥳
