#!/bin/bash NC='\033[0m' RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BROWSER=`which firefox-bin` BOOKMARKS=$HOME/.local/share/misc/bookmarks HISTORY=$HOME/.cache/omnibar_hist.txt ENGINE="https://search.dyamon.me/search?q=%s" #URL_PATTERN='^(https?://)?([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+(:[0-9]+)?(/[A-Za-z0-9%#.=-]*)*(\?([A-Za-z0-9-]+(=[A-Za-z0-9-]+)?&?)+)?$' URL_PATTERN='^(https?://)?([A-Za-z0-9-]+\.)+[A-Za-z0-9-]+(:[0-9]+)?(/.*)?' msg_info() { echo -e "${GREEN}[INFO] $1${NC}" } msg_warn() { echo -e "${YELLOW}[WARN] $1${NC}" } msg_error() { echo -e "${RED}[ERROR] $1${NC}" } print_help() { echo echo "dmenu_omnibar - a poor man's omnibar and bookmark manager (using dmenu)" echo echo "USAGE:" echo " dmenu_omnibar -h|--help" echo " dmenu_omnibar [-b|--browser ] [-B|--bookmarks ] [-s|--search-engine ]" echo echo "where:" echo " -h | --help:" echo " prints this help message." echo " -b | --browser " echo " path to the executable used to open the selected link." echo " -B | --bookmarks " echo " path to a file containing a list of bookmarks and" echo " omnibar commands. See FORMAT below." echo " -s | --search-engine " echo " default URL used when the input does not match any other" echo " bookmark, URL or omnibar command. It should contain the" echo " placeholder \"%s\" to be substituted with the query" echo " provided by dmenu." echo echo "DMENU" echo " dmenu will prompt for a bookmark from the provided list." echo " If a match is found, the URL is selected. If a match is" echo " not found the input is interpreted as follows:" echo " 1) as an URL if the provided input is an URL;" echo " 2) as a command if the input starts with \":\". Anything" echo " following the command is interpreted as a query;" echo " 3) as a query to be submitted to the default search engine." echo echo " Note: sometimes an input partially matches an entry in the" echo " bookmarks when you are aiming to one of the other behaviours" echo " (e.g., opening an URL directly). You can submit only the" echo " input text (without autoselection from the list of bookmarks)" echo " with Shift-Enter." echo echo "COMMAND HISTORY" echo " Rudimental support for command history is provided. Up to" echo " 10 unique entries will appear at the top of the dmenu" echo " selection list." echo echo " The following internal commands are also supported:" echo " !clear clears the command history" echo echo " Prepending your dmenu entry with one or more whitespaces" echo " (space or tab) disables saving the entry to history." echo echo "BOOKMARK FORMAT" echo " The bookmarks file is assumed to have the following syntax." echo echo " Lines containing a simple bookmark start with a sequence of" echo " keywords, separated by one or more spaces, followed by the" echo " URL of the bookmark." echo echo " Lines containing omnibar commands start with a command" echo " with a leading colon (e.g., \":s\", \":yt\") followed by" echo " the URL containing the placeholder \"%s\" that will be" echo " substituted with the provided query." echo } # Simple URL encoder # The conversions are performed according to these tables # # https://docs.microfocus.com/OMi/10.62/Content/OMi/ExtGuide/ExtApps/URL_encoding.htm # https://en.wikipedia.org/wiki/Percent-encoding#Reserved_characters # # Consider using something like this: # # https://gist.github.com/cdown/1163649 # # for a more reliable approach. urlencode() { sed 's|+|%2b|g' | sed 's|/|%2f|g' } # Manage history save_to_history() { # File might not exist touch "$HISTORY" # Add new entry at the top echo "🔍 $1" | cat - "$HISTORY" > "$HISTORY.tmp" # Remove duplicates and truncate file to 10 entries uniq "$HISTORY.tmp" | sed 11q > "$HISTORY" } clear_history() { shred -u "$HISTORY" "$HISTORY.tmp" } omnisearch() { [[ "$1" =~ ^[[:blank:]]+ ]] && anon=true || anon=false # Clean input input=$(echo -n "$1" | sed 's/^\s*//' | sed 's/^🔍\s*//' | sed 's/📋 \(primary\|clipboard\)\s*//') # Process input if [[ "$input" == "!clear" ]] then clear_history elif grep -qs "^$input$" "$BOOKMARKS" then # Bookmark echo "$input" | awk '{ print $(NF) }' elif [[ "$input" =~ $URL_PATTERN ]] then # URL echo "$input" $anon || save_to_history "$input" elif [[ "$input" =~ :[a-z]+\ .* ]] then # Omnisearch command CMD=$(echo "$input" | cut -s -d' ' -f1 -) SEARCH=$(echo "$input" | cut -s --complement -d' ' -f1 - | urlencode) # NOTE: the space after `$CMD` in the following `grep` is # important to avoid multiple matches when having one command # being a leading substring of another. URL=$(grep "$CMD " "$BOOKMARKS" | awk '{ print $(NF) }' | sed "s/%s/$SEARCH/g") echo "$URL" $anon || save_to_history "$input" else # Default search query SEARCH=$(echo "$input" | urlencode) echo "$ENGINE" | sed "s/%s/$SEARCH/g" $anon || save_to_history "$input" fi } while [[ $# -gt 0 ]]; do arg="$1" case $arg in -h|--help) print_help exit 0 ;; -b|--browser) if [[ $# -ge 2 ]]; then BROWSER=$2 shift ; shift else msg_error "No value found for flag $arg!" print_help exit 2 fi ;; -B|--bookmarks) if [[ $# -ge 2 ]]; then BOOKMARKS=$2 shift ; shift else msg_error "No value found for flag $arg!" print_help exit 3 fi ;; -s|--search-engine) if [[ $# -ge 2 ]]; then ENGINE="$2" shift ; shift else msg_error "No value found for flag $arg!" print_help exit 4 fi ;; *) msg_error "$arg is not a valid flag!" print_help exit 1 ;; esac done OPTIONS="" # Primary selection PRIMARY=$(xclip -out -selection primary 2>/dev/null | tr -d '\n') [ -n "$PRIMARY" ] && \ [[ "$PRIMARY" =~ $URL_PATTERN ]] && \ OPTIONS="📋 primary $PRIMARY\\n" # Clipboard selection CLIPBOARD=$(xclip -out -selection clipboard 2>/dev/null | tr -d '\n') [ -n "$CLIPBOARD" ] && \ [[ "$CLIPBOARD" =~ $URL_PATTERN ]] && \ OPTIONS="${OPTIONS}📋 clipboard $CLIPBOARD\\n" OPTIONS=$(cat "$HISTORY" ; echo -ne "$OPTIONS" ; grep -v ^: "$BOOKMARKS") URL=$(echo "$OPTIONS" | dmenu -l 18 -h 23) [ -z "$URL" ] && exit 0 || OMNIURL=$(omnisearch "$URL") # Starts browser in background [ -z "$OMNIURL" ] || "$BROWSER" "$OMNIURL" &