#!/bin/sh # # Description: A cli bookmark manager written in POSIX shell # # Author: NRK ## Copyright 2021 NRK ## Licensed under GPL v3. See LICENSE for more details. # set -x ########## # CONFIG # ########## # Appname and Version APPNAME="sxbm" VERSION="v0.4" # Where all the bookmarks get stored # Respects XDG_DATA_HOME if set DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/${APPNAME}" DATA_FILE="${DATA_DIR}/bookmarks" # Colors # For a list of ANSI color codes, check the link below # https://gist.github.com/Prakasaka/219fe5695beeb4d6311583e79933a009 COL_LINK="\033[1;31m" # Red COL_TITLE="\033[0;33m" # Yellow COL_LINE_NUM="\033[1;34m" # Blue # 1 to enable encryption, 0 to disable it # This currently does nothing, since the feature isn't implemented yet. ENCRYPTION="0" ################# ### functions ### ################# die() { [ -n "$1" ] && printf "$*\n" > /dev/stderr exit 1 } usage() { cat << EOF Usage: $APPNAME [] COMMANDS: add [title] [+tags] ls|list [-s|--strict] [-S] [-c|--disable-colors] [title] [+tags] open [-f|--force] rm|remove edit -h|--help print this text and exit -v|--version print the version and exit EOF } version() { cat << EOF $APPNAME $VERSION GPLv3 license https://codeberg.org/NRK/sxbm EOF } bm_add() { [ -z "$1" ] && die "No arguments provided. Use '$APPNAME -h' for help" while [ -n "$1" ]; do case "$1" in *.*) [ -z "$LINK" ] && LINK="$1" || die "too many links\n$LINK\n$1" ;; +*) TAGS="${TAGS}$1 " ;; *) TITLE="${TITLE}$1 " ;; esac shift done [ -z "$LINK" ] && die "No links found" echo "$LINK $TITLE $TAGS" >> "$DATA_FILE" && echo "Link $(wc -l < "$DATA_FILE") added successfully!" || die "Couldn't add link" } __awk_search() { awk -v CLINE="$COL_LINE_NUM" -v CLINK="$COL_LINK" -v CTITLE="$COL_TITLE" \ "$*"' { LINK=$1; $1=""; gsub(/[[:blank:]]\+.*($| )/,""); print CLINE NR ") " CLINK LINK " " CTITLE $0 }' "$DATA_FILE" } bm_list() { TAG_STRICT="||" TITLE_STRICT="&&" while [ -n "$1" ]; do case "$1" in "-c"|"--disable-colors") unset COL_LINK COL_TITLE COL_LINE_NUM ;; "-s"|"--strict") TAG_STRICT="&&" ;; "-S") TITLE_STRICT="||" ;; *) break ;; esac shift done while [ -n "$1" ]; do case "$1" in +*) QUERY_TAG="/[[:blank:]]\\$1($| )/ $TAG_STRICT $QUERY_TAG" ;; *) tmp_title="$(echo "$1" | tr '[:lower:]' '[:upper:]')" QUERY_TITLE="toupper(\$0)~\"[^+]$tmp_title\" $TITLE_STRICT $QUERY_TITLE" ;; esac shift done QUERY_TAG="${QUERY_TAG%$TAG_STRICT }" QUERY_TITLE="${QUERY_TITLE%$TITLE_STRICT }" if [ -n "$QUERY_TAG" ] && [ -n "$QUERY_TITLE" ]; then printf '%s\n%s\n' \ "$(__awk_search "$QUERY_TAG")" "$(__awk_search "$QUERY_TITLE")" | sort | uniq -d elif [ -n "$QUERY_TITLE" ] && [ -z "$QUERY_TAG" ]; then __awk_search "$QUERY_TITLE" else __awk_search "$QUERY_TAG" fi } bm_open() { if [ "$1" = "-f" ] || [ "$1" = "--force" ]; then FORCE=1 shift fi [ -z "$1" ] && die "No arguments given" if [ -z "$2" ] && [ "$1" -eq "$1" 2>/dev/null ]; then LINK=$(sed -n "s| .*||;${1}p;" "$DATA_FILE") else LINK=$(bm_list "-c" "$@" | cut -d " " -f 2) fi [ -n "$LINK" ] && LINK_NUM=$(echo "$LINK" | wc -l) || die "No links found" [ "$LINK_NUM" -gt 1 ] && [ -z "$FORCE" ] && die "Too many links\nUse '$APPNAME open -f' to force open them all\n\n$LINK" nohup $BROWSER $LINK 1>/dev/null 2>&1 & } bm_edit() { $EDITOR "$DATA_FILE" } bm_remove() { [ -z "$1" ] && die "No arguments given" [ "$1" -eq "$1" 2>/dev/null ] && LINK=$(sed "${1}q;d" "$DATA_FILE") || die "Link not found" printf "Delete this link? (y/n)\n$LINK\n" read ANSWER if [ "$ANSWER" = "y" ] || [ "$ANSWER" = "yes" ]; then sed -i --follow-symlinks "${1}d" "$DATA_FILE" && echo "Deleted successfully" || die "Couldn't delete link" fi } ############ ### main ### ############ [ -z "$1" ] && usage && die "No command given" [ -d "$DATA_DIR" ] || mkdir -p "$DATA_DIR" || die "Unable to create $DATA_DIR" case "$1" in "add") shift bm_add "$@" ;; "ls"|"list") shift bm_list "$@" ;; "open") shift bm_open "$@" ;; "rm"|"remove") shift bm_remove "$@" ;; "edit") shift bm_edit "$@" ;; "-h"|"--help") usage && exit 0 ;; "-v"|"--version") version && exit 0 ;; *) die "Invalid command. Use '$APPNAME -h' for help!" ;; esac