From 4de3e44012190773f5bc468bb74235f66375c2fb Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Thu, 6 Jul 2023 08:48:35 +0530 Subject: dbook: code refactoring, better ux and error management --- dbook | 401 ++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 193 insertions(+), 208 deletions(-) diff --git a/dbook b/dbook index afc54a7..5cc604b 100755 --- a/dbook +++ b/dbook @@ -2,20 +2,31 @@ book_conf="${XDG_CONFIG_HOME:-$HOME/.config}/dbook/dbook.conf" book_data="${XDG_DATA_HOME:-$HOME/.local/share}/dbook" -menu="wmenu" +w_menu="wmenu" +x_menu="dmenu" +ico_dbk=" " +ico_url="󰖟 " +ico_doc=" " +ico_pic=" " +ico_vid=" " +ico_mus="󰸪 " +ico_fil=" " +ico_txt=" " +ico_dir=" " usage() { cat <<- EOF - Usage: dbook command + Usage: dbook [ command ] [ option ] a bookmark manager using dmenu Commands: - -h show this help cruft - -i [name], inset a new entry - -s [name], inset a new entry and make a copy - -d [name], delete an entry - -t [name], type the data - -c [name], copy the date to clipboard + help show this help cruft + insert [ value [ key ] ], inset a new key + save [ value [ key ] ], inset but make a cp if it's a file + rm [ key ], delete a key + Options: + -t [ key ], type the vlaue + -c [ key ], copy the value to clipboard EOF } @@ -25,7 +36,7 @@ note() : "${1:?}" command -v notify-send > /dev/null && - notify-send " dbook" "$1" + notify-send -- "${ico_dbk} dbook" "$1" } die() @@ -37,6 +48,14 @@ die() exit "${2:-1}" } +warn() +{ + : "${1:?}" + + note "$1" + printf "\033[33;1merr: %b\033[0m\n" "$1" 1>&2 +} + dep_check() { : "${1:?}" @@ -49,72 +68,71 @@ dep_check() unset dep } -parse_name() +load_icon() { - while read -r line - do - line=${line%%|*} - - # trim leading and trailing white spaces - line=${line#"${line%%[![:space:]]*}"} - line=${line%"${line##*[![:space:]]}"} - - [ -z "${line##\#*}" ] && - continue - - echo "$line" - done < "$book_conf" + case "$(file --brief --dereference --mime-type "$1")" in + image/*) printf "%s" "$ico_pic" ;; + video/*) printf "%s" "$ico_vid" ;; + audio/*) printf "%s" "$ico_mus" ;; + text/*) printf "%s" "$ico_txt" ;; + inode/*) printf "%s" "$ico_dir" ;; + application/pdf) printf "%s" "$ico_doc" ;; + + *'No such file or directory)') + if echo "$1" | grep -qxE '^(https?://)?[^/]*\.[A-Za-z0-9]*(/.*)?'; then + printf "%s" "$ico_url" + else + printf "%s" "$ico_fil" + fi + esac } parse_data() { - read -r book_name - read_name= - - [ -z "$book_name" ] || [ ! -f "$book_conf" ] && - return 1 + # usage parse_data [ getval ] + line= + _key= + _value= while read -r line do - # trim leading and trailing white spaces - line=${line#"${line%%[![:space:]]*}"} - line=${line%"${line##*[![:space:]]}"} - - # skip trailing lines till match - case "$line" in - "$book_name"*) - # make sure read_name fully matches book_name - read_name=${line%%|*} - read_name=${read_name%"${read_name##*[![:space:]]}"} - [ "$book_name" != "$read_name" ] && - continue - ;; - *) - continue - ;; - esac + [ -z "$line" ] && continue - # extract date from string - line="${line##*|}" - line=${line#"${line%%[![:space:]]*}"} + _key="${line%%|*}" + _value="${line##*|}" - echo "$line" - return 0 + # trim leading and trailing white spaces + _key="${_key#"${_key%%[![:space:]]*}"}" + _key="${_key%"${_key##*[![:space:]]}"}" + _value="${_value#"${_value%%[![:space:]]*}"}" + _value="${_value%"${_value##*[![:space:]]}"}" + case "$_key" in + #*) continue + esac + + if [ "$1" = "getval" ]; then + if [ "$2" = "$_key" ]; then + echo "$_value" + return 0 + fi + else + echo "$(load_icon "$_value")" "$_key" + fi done < "$book_conf" - unset read_name - return 1 + unset _key _value + [ "$1" = "getval" ] && return 1 } rm_data() { - # usage rm_data [data_name] + # usage rm_data : "${1:?}" - read_name= - data= cl= + line= file= + value= old_ifs="$IFS" IFS= @@ -122,199 +140,166 @@ rm_data() do cl="$line\n" + key="${cl%%|*}" # trim leading and trailing white spaces - line=${line#"${line%%[![:space:]]*}"} - line=${line%"${line##*[![:space:]]}"} + key=${key#"${key%%[![:space:]]*}"} + key=${key%"${key##*[![:space:]]}"} # catch match - case "$line" in - "$book_name"*) - read_name=${line%%|*} - read_name=${read_name%"${read_name##*[![:space:]]}"} - - # make sure read_name fully matches - if [ "$1" = "$read_name" ] - then - # remove saved data - data="$(echo "$read_name" | parse_data)" - [ -e "$data" ] && [ -z "${data##"${book_data}"/*}" ] && - rm "$data" - - continue - fi - ;; - esac + if [ "$key" = "$1" ]; then + value="${cl##*|}" + # trim leading and trailing white spaces + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + + # delete if saved + case "$value" in + "$book_data"*) rm -f "$value" + esac + continue + fi file="${file}${cl}" - done < "$book_conf" - IFS="$old_ifs" + IFS="$old_ifs" # shellcheck disable=SC2059 printf "$file" > "$book_conf" - unset read_name data file cl old_ifs + unset cl line file value old_ifs } -entry() -{ - printf "" | "$menu" -p " ${1:?} " || - die "input empty" -} - -sh_realpath() +clip() { - # usage: sh_realpath [path] + # usage: clip [ file/text ] : "${1:?}" + : "${2:?}" - if [ -z "${1##/*}" ] - then - echo "$1" - else - echo "${PWD:-$(pwd)}/$1" + if [ -n "$WAYLAND_DISPLAY" ]; then + dep_check "wl-copy" + + [ "$1" = "text" ] && + printf "%s" "$2" | wl-copy + [ "$1" = "file" ] && + printf "file://%s" "$2" | wl-copy -t text/uri-list + elif [ -n "$DISPLAY" ]; then + dep_check "xclip" + + [ "$1" = "text" ] && + printf "%s" "$2" | xclip -selection clipboard + [ "$1" = "file" ] && + printf "file://%s" "$2" | xclip -selection clipboard "$1" -t text/uri-list fi + + note "${1} coppied to clipboard" } -clip() +set_menu() { - # usage: clip [data] - : "${1:?}" - - if [ -z "$WAYLAND_DISPLAY" ] - then - dep_check "xclip" - echo "$1" | xclip -selection clipboard + if [ -n "$WAYLAND_DISPLAY" ]; then + menu="$w_menu" + elif [ -n "$DISPLAY" ]; then + menu="$x_menu" else - dep_check "wl-copy" - echo "$1" | wl-copy - fi && - note "data coppied to clipboard" + die "tty not supported" + fi } -clip_file() +ip_menu() { - # usage: clip [data] - : "${1:?}" - - if [ -z "$WAYLAND_DISPLAY" ] - then - dep_check "xclip" - echo "$1" | xclip -selection clipboard "$1" -t text/uri-list - else - dep_check "wl-copy" - echo "$1" | wl-copy -t text/uri-list - fi && - note "data coppied to clipboard" + printf "" | "$menu" -p "${ico_dbk} ${1:?} " } -main() +key_menu() { - name= - data= - - [ -z "$WAYLAND_DISPLAY" ] && - menu="dmenu" - - [ -d "$book_data" ] || - mkdir -p "$book_data" - [ -d "$book_conf" ] || - mkdir -p "${book_conf%/*}" - - case "$1" in - -h|--help) - usage - ;; - -i) - data="${2:-$(entry name)}" - shift > /dev/null 2>&1 - shift > /dev/null 2>&1 - name="${*:-$(entry name)}" - - echo "$name" | parse_data > /dev/null && - die "name already in use" - - [ -e "$data" ] && - data="$(sh_realpath "$data")" + key="$(parse_data | "$menu" -l 25 -p "${ico_dbk} key ")" || return 1 + echo "${key#* }" +} - printf "%s\t|\t%s\n" "$name" "$data" >> "$book_conf" - ;; - -s) - data="${2:-$(entry name)}" - shift > /dev/null 2>&1 - shift > /dev/null 2>&1 - name="${*:-$(entry name)}" - echo "$name" | parse_data > /dev/null && - die "name already in use" +######## +# MAIN # +######## + +key= +value= + +[ -d "$book_data" ] || + mkdir -p "$book_data" +[ -d "$book_conf" ] || + mkdir -p "${book_conf%/*}" + +set_menu +case "$1" in +-*) ;; +"") ;; +*) command="$1" && shift +esac + +case "$command" in +insert) + value="${1:-$(ip_menu "value")}" || exit 1 + key="${2:-$(ip_menu "key")}" || exit 1 + + parse_data getval "$key" > /dev/null && + die "key already in use" + [ -e "$value" ] && + value="$(realpath "$value")" + + printf "%s\t|\t%s\n" "$key" "$value" >> "$book_conf" + ;; +save) + value="${1:-$(ip_menu "value")}" || exit 1 + key="${2:-$(ip_menu "key")}" || exit 1 + + parse_data getval "$key" > /dev/null && + die "key already in use" + [ -d "$value" ] && + warn "${value} is a directory, will not be saved" + + if [ -f "$value" ]; then + cp "$value" "$book_data" + value="${book_data}/$(basename "$value")" + fi - [ -d "$data" ] && - die "$data is a directory, use -i instead" + printf "%s\t|\t%s\n" "$key" "$value" >> "$book_conf" + ;; +rm) + key="${1:-$(key_menu)}" || exit 1 + rm_data "$key" + ;; +"") + dep_check "xdg-open" + [ -s "$book_conf" ] || + die "no bookmarks, try dbook -h" + + key="${2:-$(key_menu)}" || exit 1 + value="$(parse_data getval "$key")" || + die "${key}: no such key" - if [ -e "$data" ] - then - cp "$data" "$book_data" - data="${book_data}/${data##*/}" - fi - - printf "%s\t|\t%s\n" "$name" "$data" >> "$book_conf" - ;; - -d) - rm_data "${2:-"$(parse_name | "$menu" -p " " -l 25)"}" - ;; + case "$1" in -t) - shift > /dev/null 2>&1 - data="${*:-"$(parse_name | "$menu" -p " " -l 25 | parse_data)"}" - [ -z "$data" ] && - die "empty, use -i to add an entry" - if [ -z "$WAYLAND_DISPLAY" ] then dep_check "xdotool" - xdotool type --delay 20 "$data" + xdotool type --delay 20 "$value" else dep_check "wtype" - wtype -d 20 "$data" + wtype -d 20 "$value" fi ;; -c) - shift > /dev/null 2>&1 - data="${*:-"$(parse_name | "$menu" -p " " -l 25 | parse_data)"}" - [ -z "$data" ] && - die "empty, use -i to add an entry" - - case "$(file --brief "$data")" in - *ASCII\ text*) - clip "$(cat "$data")" - ;; - *No\ such\ file\ or\ directory\)) - clip "$data" - ;; - *) - clip_file "$(printf "file://%s" "$data")" - ;; + case "$(file --brief --dereference --mime-type "$value")" in + text/*) clip text "$(cat "$value")" ;; + *'No such file or directory)') clip text "$value" ;; + *) clip file "$value" esac ;; "") - dep_check "xdg-open" - [ -s "$book_conf" ] || - die "no bookmarks, try dbook -h" - - data="$(parse_name | "$menu" -p " " -l 25 | parse_data)" - [ -z "$data" ] && - die "empty, use -i to add an entry" - - xdg-open "$data" 2> /dev/null || - case "$(file --brief "$data")" in - *ASCII\ text*) - clip "$(cat "$data")" - ;; - *) - clip "$data" - ;; - esac - ;; + xdg-open "$value" 2> /dev/null || clip text "$value" ;; *) - die "$1, invalid usage" + die "${1}: invalid option" esac -} - -main "$@" + ;; +help) usage ;; +*) die "$command, invalid command" +esac -- cgit v1.2.3