rofi/scripts/rofi-websearch 2.9 K raw
1
#!/bin/sh
2
# Requires: rofi, a browser
3
# Web search: pick an engine, type a query, land in the browser. Typing a
4
# query straight into the engine list also works — anything that is not one
5
# of the listed engines is treated as a query for the first one.
6
# Add engines by adding lines to ENGINES: "<icon>  <name>\t<url with %s>".
7
set -u
8
9
BROWSER_CMD=${BROWSER:-}
10
if [ -z "$BROWSER_CMD" ]; then
11
    for candidate in helium.AppImage chromium firefox qutebrowser xdg-open; do
12
        if command -v "$candidate" >/dev/null 2>&1; then
13
            BROWSER_CMD=$candidate
14
            break
15
        fi
16
    done
17
fi
18
[ -n "$BROWSER_CMD" ] || { notify-send "Web search" "No browser found"; exit 1; }
19
20
# The AppImage refuses to start under this user without it, the same way the
21
# Mod+O keybinding launches it.
22
BROWSER_ARGS=""
23
case "$BROWSER_CMD" in
24
    *helium*) BROWSER_ARGS="--no-sandbox" ;;
25
esac
26
27
ENGINES=$(printf '%s\n' \
28
    "󰖟  kagi	https://kagi.com/search?q=%s" \
29
    "󰊤  github	https://github.com/search?q=%s" \
30
    "󰛦  npm	https://www.npmjs.com/search?q=%s" \
31
    "󱘗  crates.io	https://crates.io/search?q=%s" \
32
    "󰅴  mdn	https://developer.mozilla.org/en-US/search?q=%s" \
33
    "󱆃  alpine packages	https://pkgs.alpinelinux.org/packages?name=%s" \
34
    "󰖬  wikipedia	https://en.wikipedia.org/w/index.php?search=%s" \
35
    "󰗃  youtube	https://www.youtube.com/results?search_query=%s" \
36
    "󰅩  stack overflow	https://stackoverflow.com/search?q=%s")
37
38
# Percent-encode by byte, so a query with an accent or an emoji survives the
39
# trip. od gives the bytes; awk decides which ones may travel as themselves.
40
urlencode() {
41
    printf '%s' "$1" | od -An -tx1 -v | tr -s ' ' '\n' | awk '
42
        function h2d(h,   i, c, d) {
43
            d = 0
44
            for (i = 1; i <= length(h); i++) {
45
                c = index("0123456789abcdef", substr(tolower(h), i, 1)) - 1
46
                d = d * 16 + c
47
            }
48
            return d
49
        }
50
        NF {
51
            d = h2d($1)
52
            ch = sprintf("%c", d)
53
            if (ch ~ /^[A-Za-z0-9._~-]$/) printf "%s", ch
54
            else printf "%%%s", toupper($1)
55
        }
56
    '
57
}
58
59
CHOICE=$(printf '%s\n' "$ENGINES" | cut -f1 | rofi -dmenu -i -l 9 -p "search") || exit 0
60
[ -n "$CHOICE" ] || exit 0
61
62
# rofi hands back whatever was typed when nothing matched: that is a query
63
# for the default engine, not an engine name.
64
URL=$(printf '%s\n' "$ENGINES" | awk -F'\t' -v c="$CHOICE" '$1 == c { print $2; exit }')
65
if [ -n "$URL" ]; then
66
    QUERY=$(rofi -dmenu -p "${CHOICE##*  }" -l 0 </dev/null) || exit 0
67
else
68
    QUERY=$CHOICE
69
    URL=$(printf '%s\n' "$ENGINES" | awk -F'\t' 'NR == 1 { print $2; exit }')
70
fi
71
[ -n "$QUERY" ] || exit 0
72
73
# A pasted or typed URL is a destination, not something to search for.
74
case "$QUERY" in
75
    http://*|https://*) TARGET=$QUERY ;;
76
    *) TARGET=$(printf "$URL" "$(urlencode "$QUERY")") ;;
77
esac
78
79
exec "$BROWSER_CMD" ${BROWSER_ARGS:+$BROWSER_ARGS} "$TARGET"