#!/usr/bin/env bash
# ==============================================================
# browser - Simple browser dispatcher / launcher
# --------------------------------------------------------------
# Author : Kris Yotam
# Contact : krisyotam@protonmail.com
# License : GNU GPLv3
# Created : 2025-10-27
# --------------------------------------------------------------
# Description:
# Suckless, functionized launcher that routes URLs to the
# appropriate browser or lets the user pick a browser from a
# tiny TUI. Usage:
# browser # interactive chooser
# browser <name> # launch specific browser (librewolf, brave, tor, surf)
# browser <url> # dispatch URL to the preferred browser
# browser <name> <url> # open URL with chosen browser
#
# Dispatch rules (default):
# - .onion -> TOR
# - blog platforms (blogspot, medium, wordpress, ghost) -> surf
# - otherwise -> librewolf
# ==============================================================
set -euo pipefail
# -------------------- Configuration (like config.py header) -----------------
# Commands (override with env vars if needed)
LIBREWOLF_CMD="${LIBREWOLF_CMD:-librewolf}"
BRAVE_CMD="${BRAVE_CMD:-brave-browser}"
TOR_CMD="${TOR_CMD:-torbrowser-launcher}"
SURF_CMD="${SURF_CMD:-surf}"
# Supported friendly names (lowercase)
BROWSERS=(librewolf brave tor surf)
# ---------------------------------------------------------------------------
print_help() {
cat <<'HELP'
browser - Simple browser dispatcher / launcher
Usage:
browser # interactive chooser
browser <name> # launch specific browser (librewolf, brave, tor, surf)
browser <url> # dispatch URL to the preferred browser
browser <name> <url> # open URL with chosen browser
Examples:
browser https://example.com
browser surf https://blog.example.com
browser brave
HELP
}
choose_one() {
# choose_one <prompt> <opt1> <opt2> ...
local prompt="$1"; shift
local opts=("$@")
local i=1
printf "%s\n" "$prompt"
for o in "${opts[@]}"; do
printf " %d) %s\n" "$i" "$o"
i=$((i+1))
done
printf "Choice [1-%d]: " $((i-1))
local choice
IFS= read -r choice || choice=''
if ! printf '%s' "$choice" | grep -qE '^[0-9]+$'; then
return 1
fi
if [ "$choice" -lt 1 ] || [ "$choice" -ge "$i" ]; then
return 1
fi
echo "${opts[$((choice-1))]}"
}
is_url() {
# crude URL test: contains :// or starts with www. or contains a dot and no spaces
local s="$1"
case "$s" in
*://*|www.*|*.*) return 0 ;;
*) return 1 ;;
esac
}
extract_host() {
# accept full URL or bare host
local u="$1"
# strip protocol
u="${u#*://}"
# strip path
u="${u%%/*}"
# strip port
u="${u%%:*}"
printf '%s' "$u"
}
dispatch_url() {
local url="$1"
local host
host=$(extract_host "$url")
# lower-case host for pattern matching
local host_lc
host_lc=$(printf '%s' "$host" | tr '[:upper:]' '[:lower:]')
case "$host_lc" in
*.onion)
launch_browser "tor" "$url" ;;
*blogspot.*|*medium.*|*wordpress.*|*ghost.*)
launch_browser "surf" "$url" ;;
*)
launch_browser "librewolf" "$url" ;;
esac
}
launch_browser() {
# launch_browser <name> [url]
local name="$1"
local url="${2:-}"
local cmd=""
case "${name,,}" in
librewolf)
cmd="$LIBREWOLF_CMD" ;;
brave)
cmd="$BRAVE_CMD" ;;
tor)
cmd="$TOR_CMD" ;;
surf)
cmd="$SURF_CMD" ;;
*)
printf "Unknown browser: %s\n" "$name" >&2
return 2 ;;
esac
if [ -n "$url" ]; then
# prefer to open in background so script exits
setsid "$cmd" "$url" >/dev/null 2>&1 &
else
setsid "$cmd" >/dev/null 2>&1 &
fi
}
main() {
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
print_help
return 0
fi
if [ "$#" -eq 0 ]; then
# interactive chooser
local choice
choice=$(choose_one "Choose browser:" "${BROWSERS[@]}") || {
printf "No valid choice. Exiting.\n"; return 1
}
launch_browser "$choice"
return 0
fi
# If first arg matches a browser name -> explicit launch
local first="${1,,}"
if printf '%s\n' "${BROWSERS[@]}" | grep -Fxq -- "$first"; then
if [ "$#" -ge 2 ]; then
# browser name + url
launch_browser "$first" "$2"
else
launch_browser "$first"
fi
return 0
fi
# If first arg looks like a url -> dispatch
if is_url "$1"; then
dispatch_url "$1"
return 0
fi
# otherwise assume it's a hostname-like argument; dispatch
dispatch_url "$1"
}
# If executed directly, run main with positional args
if [ "${BASH_SOURCE[0]:-}" = "$0" ]; then
main "$@"
fi