2024-08-20 18:25:13 +01:00

926 lines
38 KiB
Bash
Executable File

#!/usr/bin/env bash
#-*-coding:utf-8 -*-
#Auto updated?
# Yes
#File:
# bm
#Author:
# The-Repo-Club [wayne6324@gmail.com]
#Github:
# https://github.com/The-Repo-Club/
#
#Created:
# Fri 09 December 2022, 07:19:23 AM [GMT]
#Modified:
# Fri 14 July 2023, 11:47:25 PM [GMT+1]
#
#Description:
# <Todo>
#
#Dependencies:
# <None>
#
# shellcheck disable=all
# default filename for Baker config file (just the filename no path)
readonly _BM_DEFAULT_CONFIG_FILENAME="${_BM_DEFAULT_CONFIG_FILENAME:=bm.conf}"
config() {
echo "Reading Configuration..."
[[ "${__w:=$HOME}" != "${HOME}" ]] && echo "Working dir changed to ${__w}"
[[ "${__w: -1}" != '/' ]] && __w="${__w//\/\//\/}/"
if [ ${__B:=0} -eq 0 ]; then
# If a config file is given, we take it first, or look for a default config file
export _BM_CONFIG_FILE="${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
if [ "$(uname -s)" = 'Linux' -a -f "$(readlink -m "${1:-}" 2>/dev/null)" ]; then
export _BM_CONFIG_FILE="${1}"
. "${1}"
elif [ -e "${1}" ]; then
export _BM_CONFIG_FILE="${1}"
. "${1}"
elif [ ${__n:=0} -eq 0 -a -f "${__w}${_BM_DEFAULT_CONFIG_FILENAME}" ]; then
. "${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
echo "Default config file loaded ${__w}${_BM_DEFAULT_CONFIG_FILENAME}"
fi
else
export _BM_CONFIG_FILE='none'
fi
# _BM_BOOKMARK_FILE : Where is stored the bookmark file
export _BM_BOOKMARK_FILE="${_BM_BOOKMARK_FILE:=${__w}bm.bm}"
# _BM_BOOKMARK_BACKUP_FILE : Where is stored the backup of bookmark file
export _BM_BOOKMARK_BACKUP_FILE="${_BM_BOOKMARK_BACKUP_FILE:=${__w}.bm.bm.bck}"
# _BM_HTML_FILE : Location of generated HTML file
export _BM_HTML_FILE="${_BM_HTML_FILE:=${__w}bm.html}"
# _BM_DELETE_FILE : Where to find the deleted URLs
export _BM_DELETE_FILE="${_BM_DELETE_FILE:=${__w}.bm.trash}"
# _BM_BOOKMARK_BACKUP : Copy the bookmark file before each modification
export _BM_BOOKMARK_BACKUP=${_BM_BOOKMARK_BACKUP:=false}
# _BM_BOOKMARK_BACKUP_VERSION : How many copy to keep (0 for 1, 1 for 2, ...)
export _BM_BOOKMARK_BACKUP_VERSION=${_BM_BOOKMARK_BACKUP_VERSION:=0}
# _BM_DELETE_ALLOWED : Even with the force flag the delete function will not be allowed (true or false)
export _BM_DELETE_ALLOWED=${_BM_DELETE_ALLOWED:=true}
# _BM_DELETE_TO_FILE : Allow or disallow the deleted URLs to be copied in the Trash (true or false)
export _BM_DELETE_TO_FILE=${_BM_DELETE_TO_FILE:=false}
# _BM_DELETE_PICTURE : Allow or disallow the picture associated to a URL to be deleted (true or false)
export _BM_DELETE_PICTURE=${_BM_DELETE_PICTURE:=true}
# _BM_DUPS_DETECT : Look for duplicates URL before insert in bookmark file (ovverided by -F)
export _BM_DUPS_DETECT=${_BM_DUPS_DETECT:=true}
# _BM_ASK_BEFORE_OPEN : When using bm with a URL (or search term) ask before opening the browser if more than one answer
export _BM_ASK_BEFORE_OPEN=${_BM_ASK_BEFORE_OPEN:=true}
# _BM_GET_PAGE_TITLE : When adding a URL execute a curl command to get the <title> if none given
export _BM_GET_PAGE_TITLE=${_BM_GET_PAGE_TITLE:=true}
# _BM_SCREENSHOT_DIRECTORY : Where to store the screenshot taken
export _BM_SCREENSHOT_DIRECTORY="${_BM_SCREENSHOT_DIRECTORY:=${__w}.bm.shots}"
# _BM_SCREENSHOT_GET : When adding a URL try to take a screenshot in background
export _BM_SCREENSHOT_GET=${_BM_SCREENSHOT_GET:=true}
# _BM_SCREENSHOT_WAIT : When taking a screenshot wait for the action to finish
export _BM_SCREENSHOT_WAIT=${_BM_SCREENSHOT_WAIT:=false}
# _BM_OPEN_FIRST : When using open, the first link found is open
# Also work with the copy to clipboard function
export _BM_OPEN_FIRST=${_BM_OPEN_FIRST:=false}
# _BM_OPEN_ALL : When using open, if more than one result, all are open (with the same function)
# Also work with the copy to clipboard function (overrided by -Y)
export _BM_OPEN_ALL=${_BM_OPEN_ALL:=false}
# _BM_NO_ARGS_FORCE_HELP : If no args given to script sends to help instead of list all link
export _BM_NO_ARGS_FORCE_HELP=${_BM_NO_ARGS_FORCE_HELP:=false}
# _BM_CREATE_BOOKMARK_FILE : If bm is called to list and no file exists. bm will create a default one.
export _BM_CREATE_BOOKMARK_FILE=${_BM_CREATE_BOOKMARK_FILE:=true}
# _BM_CMD_CAPTURE_CHECK : If you don't have tool installed to take screenshot, just put to false.
# It will disable the _BM_GET_SCREENSHOT.
export _BM_CMD_CAPTURE_CHECK=${_BM_CMD_CAPTURE_CHECK:=true}
# I suppose the uname command is available
# Please note, the PRE & POST capture command will not be analyzes nor verified.
if [ "$(uname -s)" = 'Linux' ]; then
export _BM_CMD_IMAGE_OPEN='feh'
export _BM_CMD_OPEN='xdg-open'
export _BM_CMD_PRE_CAPTURE=''
export _BM_CMD_CAPTURE='cutycapt'
export _BM_CMD_CAPTURE_ARGS='--insecure --url={URL} --out={FILE}'
export _BM_CMD_POST_CAPTURE=''
export _BM_CMD_MD5='md5sum'
export _BM_CMD_COPY='xsel'
else
# To keep commpatibility with the Mac (even i don't have any)
export _BM_CMD_IMAGE_OPEN='open'
export _BM_CMD_OPEN='open'
export _BM_CMD_PRE_CAPTURE=''
export _BM_CMD_CAPTURE='webkit2png'
export _BM_CMD_CAPTURE_ARGS='-C -o {FILE}.png {URL}'
export _BM_CMD_POST_CAPTURE='cp -- "{FILE}-clipped.png" "{FILE}" ; rm -f -- {FILE}-{clipped,full,thumb}.png'
export _BM_CMD_MD5='md5'
export _BM_CMD_COPY='xsel'
fi
# _BM_PRINT_LINE : This line is used to print datas
export _BM_PRINT_LINE="${_BM_PRINT_LINE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET}}"
# _BM_PRINT_LINE_ALTERNATIVE : This line is used to print datas
export _BM_PRINT_LINE_ALTERNATIVE="${_BM_PRINT_LINE_ALTERNATIVE:=${BOLD}${GRAY}BM_ACCEL ${RESET}${DIM}-->${RESET} ${GRAY_LIGHT}BM_TITLE ${GRAY_DARK}${BOLD}[BM_TAGS]${RESET}\n${UNDERLINE}${DIM}BM_URL${RESET} ${GRAY_DARK}(BM_MD5)${RESET} - ${GRAY_DARK}Added: BM_DATE${RESET} - ${GRAY_DARK}Picture: BM_PICTURE${RESET}}"
# _BM_PRINT_SCHEME : If set to no, bm will remove scheme from URL printing
export _BM_PRINT_SCHEME=${_BM_PRINT_SCHEME:=true}
# _BM_SEARCH_IGNORECASE : Ignore case if set when searching (overrided by -i)
export _BM_SEARCH_IGNORECASE=${_BM_SEARCH_IGNORECASE:=false}
}
# slug creates a friendly URL like 'hello-world'
slug() {
iconv -f utf8 -t ascii//TRANSLIT | tr -cs '[:alnum:]\n' - | tr '[:upper:]' '[:lower:]' | sed 's|^-*||;s|-*$||'
}
# version
RELEASE='$Format:%h$'
VERSION='2021.04.05'
#
# Output usage info
#
usage() {
cat <<EOF
Usage: ${0} [modifier(s)] command [option(s)]
Commands :
==========
-h Print this help
-v/-V Print the version/And the short commit name
-a 'URL' Add the URL to bookmark file
Options for -a
-t "TagList" Tags are sparated by a comma ,
-T "Title" Title for this URL (if empty and allowed Title
downloaded)
-A "accel" Accelerator when search for URLs (start with Accel)
Default is an autoincrement index
-F Force the bookmark to be created (even if duplicate
or invalid)
-p Force the screenshot to be taken (config dependant)
-l List all URLs (default action, same thing as calling bm without args)
Options for -l
-z Use the alternate print list
-n Sort the results by date
-N Sort the results by date (reverse)
-s 'object' Search for bookmarks
Options for -s
-i Incensitive case search
-z Use the alternate print list
-n Sort the results by date
-N Sort the results by date (reverse)
Objects are
:string Search in accelerator list
+string Search in tags list
=string Search in MD5 list
/string Search in URL list
string Search in full test
-o 'object' Search for bookmarks and open it (use the same argument as for -s)
Options for -o
-i Incensitive case search
-O If more than one answer force the first bookmark to be open
-Y If more than one answer force ALL bookmarks to be open
-x 'object' Search for bookmarks and copy it to clipboard (use the same argument as for -s)
Options for -x
-i Incensitive case search
-X If more than one answer force the first bookmark to be copied
-Y If more than one answer force ALL bookmarks to be copied
-r 'object' Search for bookmarks and Print the recorded associated picture (use the same argument as for -s)
Options for -r
-i Incensitive case search
-O If more than one answer force the first bookmark to have its picture printed
-Y If more than one answer force ALL bookmarks to have their picture printed
-d 'URL' Delete the URL from bookmark file
md5sum or
'URL part' Options for -d
-D Delete first occurence only
-F Force the bookmark to be deleted (even if duplicate)
-p Delete the associated picture (no trash available)
-g Generate a HTML page with all bookmarks
If used more than once, generate a page per tag
Options for -g
-G "filename" If g==1 then generate and use this filename to generate page
-O Open the file when generated
-P 'object' Generate all Non existant picture (check done for all URL in bm), if none argument.
If an argument is given (use the same argument as for -s) only the results will have a picture.
Options for -P
-F Force the picture to be taken again (even if already exists).
-q 'object' Connect to the URL to replace the existing Title by the downloaded one.
Options for -q
-i Incensitive case search
-O If more than one answer force the first bookmark to have its title downloaded
-Y If more than one answer force ALL bookmarks to have their title downloaded
-L List all tags
-C Print the color table (usefull for configuration)
-E Open the bm.bm with your \$EDITOR
-S Show statistics about bookmarks/tags (and also configuration)
Options for -S
-p Print the list of orphaned Pictures
Modifiers :
===========
-c 'file' Use this configuration file instead of the default one
-b 'file' Use this bookmark file instead of the default one
-w 'directory' Use this directory to find default configuration file and bookmark file instead of the default one
EOF
}
getAccelMax() {
while read a; do
(( ${a:=0} >= ${_max:=0} )) && (( _max=${a}+1 ))
done <<< "$( awk -F'|' '$0 !~ /^[ ]*#/ && $3 ~ /^[:space:]*:[:space:]*[0-9]+[:space:]*$/ { sub(/:+/, "", $3); print $3; }' "${_BM_BOOKMARK_FILE}")"
echo -n "${_max}"
}
backupBm() {
if [ "${_BM_BOOKMARK_BACKUP,,}" = 'true' ]; then
while [ ${count:=${_BM_BOOKMARK_BACKUP_VERSION}} -gt 0 ]; do
cp -- "${_BM_BOOKMARK_BACKUP_FILE}.$(( count-1 ))" "${_BM_BOOKMARK_BACKUP_FILE}.$(( count-- ))" 2>&1 >/dev/null
done
cp -- "${_BM_BOOKMARK_FILE}" "${_BM_BOOKMARK_BACKUP_FILE}.0"
fi
}
titleDl() {
if command -v hxselect &>/dev/null ; then
curl -Lks "${1:-}" 2>&1 | hxselect -ic title 2>/dev/null
else
curl -Lks "${1:-}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i'
fi
}
saveUrl() {
[[ "${__url}" =~ ^[[:space:]]*(f|ht)tps*://.*$ ]] || {
if [ ${__F:=0} -eq 0 ]; then
die "Your URL seems invalid '${__url}'. Use -F to force."
else
echo "Force adding invalid URL" >&2
fi
}
local _sum="$(${_BM_CMD_MD5} <<< "${__url}")"
if [ "${_BM_DUPS_DETECT,,}" = 'true' ]; then
if [ ! -z "$(awk -F '|' '$0 !~ /^[ ]*#/ && $1 ~ /'"${_sum%% *}"'/' "${_BM_BOOKMARK_FILE}")" ]; then
if [ ${__F:=0} -eq 0 ]; then
die "URL is already in the Bookmark file"
else
echo "Force adding duplicate URL" >&2
fi
fi
fi
if [ "${_BM_GET_PAGE_TITLE,,}" = 'true' -a -z "${__T:=}" ]; then
# if [ $( command -v hxselect &>/dev/null ) ]; then
# __T="$( curl -Lks "${__url}" 2>&1 | hxselect -ic title )"
# else
# __T="$( curl -Lks "${__url}" 2>&1 | sed '/<title>/I!d;/<\/title>/I!d;s;^[[:space:]]*<title>\([^<]*\)<.*;\1;i' )"
# fi
__T="$( titleDl "${__url}" )"
fi
[[ ! -z "${__T:=}" ]] && __T="${__T//\|/\\|}"
backupBm
[[ -z "${__T:=}" ]] && __T="$(slug <<< "${__url}")" # If no title => slug the url
[[ -z "${__t:=}" ]] && __t='default' # Default tag is default
[[ -z "${__A:=}" ]] && __A="$(getAccelMax)"
[[ "${__A:0:1}" != ':' ]] && __A=":${__A}" # If no accel => Add a number
echo "${_sum%% *}|$(date '+%FT%TZ')|${__A}|${__url}|${__T}|${__t}" >> "${_BM_BOOKMARK_FILE}" || die "Insert aborted into '${_BM_BOOKMARK_FILE}' !"
echo "${__url} inserted into '${_BM_BOOKMARK_FILE}'"
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${_sum%% *}.png" "${__url}" & }
[[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait
search_bookmarks "${__A}"
}
#
# manage each line to print it
#
readLines() {
IFS='|'
while read m d a u T t; do
_line="${_BM_PRINT_LINE}"
[[ ${__z:=0} -eq 1 ]] && _line="${_BM_PRINT_LINE_ALTERNATIVE}"
_line="${_line//BM_MD5/$m}"
if [ "${_BM_PRINT_SCHEME,,}" = 'false' ]; then
u="$(sed 's/^[[:space:]]*\(f\|ht\)tps*:\/\///' <<< "${u}")"
fi
_line="${_line//BM_URL/$u}"
_line="${_line//BM_TITLE/$T}"
_line="${_line//BM_TAGS/$t}"
_line="${_line//BM_ACCEL/${a:=No-Accelerator}}"
_line="${_line//BM_DATE/${d}}"
local _pict="$(stat -c '%y' "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" 2>/dev/null)"
[[ ! -z "${_pict}" ]] && _pict="$(date '+%FT%TZ' --date "${_pict}")"
_line="${_line//BM_PICTURE/${_pict:=None}}"
echo -e "${_line}\n"
done
}
#
# List all bookmarks
#
list_bookmarks() {
[[ ${__n:=0} -eq 1 ]] && { awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 | readLines; return; }
[[ ${__N:=0} -eq 1 ]] && { awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | sort -t'|' -k2 -r | readLines; return; }
awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | readLines
}
#
# Search all bookmarks with <query>
#
search() {
local _s=''
[[ -z "${1:-}" ]] && list_bookmarks && return
case "${1:0:1}" in
:) _s=3; _ss=0 ;; # Accelerator
+) _s="NF"; _ss=1 ;; # Tags
=) _s=1; _ss=1 ;; # MD5 part
/) _s=4; _ss=1 ;; # URL part
*) _s=0; _ss=0 ;; # full text search
esac
[[ "${_BM_SEARCH_IGNORECASE,,}" = 'true' || ${__i:=0} -eq 1 ]] && local _ign=1
awk -F'|' -vIgn=${_ign:=0} '
BEGIN {
if (Ign == 1) { s=tolower("'"${1:${_ss}}"'"); } else { s="'"${1:${_ss}}"'"; }
}
$0 !~ /^[ ]*#/ && (Ign == 0 && $'"${_s}"' ~ s) || (Ign ==1 && tolower($'"${_s}"') ~ s) {print $0;}
' "${_BM_BOOKMARK_FILE}"
}
search_bookmarks() {
[[ ${__n:=0} -eq 1 ]] && { search "${@}" | sort -t'|' -k2 | readLines; return; }
[[ ${__N:=0} -eq 1 ]] && { search "${@}" | sort -t'|' -k2 -r | readLines; return; }
search "${@}" | readLines
}
#
# Open first bookmark matching <query>
#
recorded_picture() {
local _lines="$(search "${@}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
read -p"More than one URL found. Open all Pictures ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
fi
fi
local _all=''
echo ''
while read _nl; do
readLines <<< "${_nl}"
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
_all="${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<< "${_nl}").png ${_all}"
else
"${_BM_CMD_IMAGE_OPEN}" "${_BM_SCREENSHOT_DIRECTORY}/$(awk -F'|' '{print $1}' <<< "${_nl}").png"
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
fi
done <<< "$(echo -e "${_lines}")"
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_IMAGE_OPEN}" "${_all}"
}
download_title() {
local _lines="$(search "${@}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
read -p"More than one URL found. Download Title for all URLs ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
fi
fi
local _all=''
echo ''
while read _nl; do
readLines <<< "${_nl}"
local u="$(awk -F'|' '{print $4}' <<< "${_nl}")"
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
echo "${u} --> $( titleDl "${u}" )"
else
echo "${u} --> $( titleDl "${u}" )"
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
fi
done <<< "$(echo -e "${_lines}")"
}
open_bookmark() {
local _lines="$(search "${@}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
fi
fi
local _all=''
while read _nl; do
readLines <<< "${_nl}"
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
_all="$(awk -F'|' '{print $4}' <<< "${_nl}") ${_all}"
else
"${_BM_CMD_OPEN}" "$(awk -F'|' '{print $4}' <<< "${_nl}")"
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
fi
done <<< "$(echo -e "${_lines}")"
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${_all}"
}
copy_bookmark() {
local _lines="$(search "${@}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__X:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
read -p"More than one URL found. Copy all ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -X to force the first URL or refine your search"
fi
fi
local _all=''
while read _nl; do
readLines <<< "${_nl}"
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
_all="$(awk -F'|' '{print $4}' <<< "${_nl}") ${_all}"
else
awk -F'|' '{print $4}' <<< "${_nl}" | "${_BM_CMD_COPY}"
awk -F'|' '{print $4}' <<< "${_nl}" | "${_BM_CMD_COPY}" -b
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__X:=0} -eq 1 ]] && exit 0
fi
done <<< "$(echo -e "${_lines}")"
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" <<< "${_all}"
[[ "${_BM_OPEN_ALL,,}" = 'true' || ${__Y:=0} -eq 1 ]] && "${_BM_CMD_COPY}" -b <<< "${_all}"
}
delete_bookmark() {
[[ -z "${@//[ ]/}" ]] && die "You MUST give an argument !"
local _lines="$(search "${@}")"
if [ "${_BM_DELETE_ALLOWED,,}" = 'true' ]; then
while read _nl; do readLines <<< "${_nl}"; done <<< "$(echo -e "${_lines}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__D:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -ge 1 ]; then
read -p"You're about to delete entry(ies). Ready ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -D to force the backup if not configured."
echo ''
fi
fi
backupBm
local _all=''
while read _nl; do
if [ "${_BM_DELETE_TO_FILE,,}" = 'true' -o ${__D:=0} -eq 1 ]; then
echo "${_nl}" >> "${_BM_DELETE_FILE}"
fi
[[ "${_BM_DELETE_PICTURE,,}" = 'true' || ${__p:=0} -eq 1 ]] && rm -f -- "${_BM_SCREENSHOT_DIRECTORY}/${_nl%%|*}.png"
IFS='|' read m d a u T t <<< "${_nl}"
sed -i -e '/^'"${m}.*${u//\//\\/}.*${t//\//\\/}"'$/d' "${_BM_BOOKMARK_FILE}"
done <<< "$(echo -e "${_lines}")"
else
die "You're not allowed to delete entries. Change the _BM_DELETE_ALLOWED to true !"
fi
}
edit_bookmark() {
echo -e "You're about to open your '${_BM_BOOKMARK_FILE}' file with your \$EDITOR.\n\n/!\ Press any key to continue, or CTRL+C to abort. /!\ "
read -n1 toto
${EDITOR:=vi} "${_BM_BOOKMARK_FILE}"
}
#
# Diplay some statistics about the bookmarks
#
stats() {
echo "===== Configuration ====="
echo "Bookmark file : ${_BM_BOOKMARK_FILE}"
echo "Trash file : ${_BM_DELETE_FILE}$([[ ! -e "${_BM_DELETE_FILE}" ]] && echo " (but doesn't exist)")"
echo "Config file : ${_BM_CONFIG_FILE}$([[ ! -e "${_BM_CONFIG_FILE}" ]] && echo " (but doesn't exist)")"
echo "Screenshot directory : ${_BM_SCREENSHOT_DIRECTORY}"
echo "Backup file(s) : ${_BM_BOOKMARK_BACKUP_FILE}*"
for i in "${_BM_BOOKMARK_BACKUP_FILE}"*; do
echo " ${i}"
done
echo -e "\n===== Statistics ====="
readarray -t lines < "${_BM_BOOKMARK_FILE}"
echo "# of Bookmarks : ${#lines[@]}"
echo "# of Duplicate : $(awk -F'|' '$0 !~ /^[ ]*#/ {print $1}' "${_BM_BOOKMARK_FILE}" |sort |uniq -d |wc -l)"
local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')"
echo "# of tags : $( sort -u <<< "${tags,,}" |wc -l)"
echo "Top 14 tags used :"
local nli=0;local pa=0
(
sort <<< "${tags,,}" | uniq -c | sort -nr | while read n t; do
(( nli++ ));[[ ${nli} -gt 7 ]] && nli=1 && echo ''
(( pa++ ));[[ ${pa} -gt 14 ]] && break
echo -n "${t// / }:${n} "
done
echo ''
) | column -t -c 17
echo "# of Pictures : $(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/*.png |wc -l) [# of files in ${_BM_SCREENSHOT_DIRECTORY}:$(ls -1 "${_BM_SCREENSHOT_DIRECTORY}"/* |wc -l)]"
echo "All Pictures size : $(du -sh "${_BM_SCREENSHOT_DIRECTORY}" | awk '{print $1}')"
local orphaned="$( cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && ((orph++)); done; echo "${orph:=0}" )"
local nopics="$( while read i; do [[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${i%%|*}.png" ]] && ((nopics++)); done <<< ${lines[@]}; echo "${nopics:=0}" )"
echo "Bookmark Without Pic : ${nopics}"
echo "Orphaned pictures : ${orphaned}"
[[ ${__p:=0} -eq 1 ]] && { echo -e "\n===== Orphaned ====="; echo "List of orphaned pictures :"; cd "${_BM_SCREENSHOT_DIRECTORY}" && for i in *; do [[ -z "$(sed -e '/'"${i%%.*}"'/!d' "${_BM_BOOKMARK_FILE}")" ]] && echo " - ${_BM_SCREENSHOT_DIRECTORY}/${i} [Trashed URL should be: $(awk -F'|' 'BEGIN{l="Unknown"} ($1 ~ /'"${i%%.*}"'/) {l=$4;} END {print l}' "${_BM_DELETE_FILE}")]"; done; };
}
#
# Output tags.
#
list_tags() {
local tags="$(awk -F'|' '$0 !~ /^[ ]*#/ {print $NF}' "${_BM_BOOKMARK_FILE}" | sed -e 's/\([[:space:]]*,[[:space:]]*\)/\n/g')"
local nli=0
(
sort <<< "${tags,,}" | uniq -c | sort -nr | while read n t; do
(( nli++ ));[[ ${nli} -gt 7 ]] && nli=1 && echo ''
echo -n "${t// / }:${n} "
done
echo ''
) | column -t -c 17
}
#
# Stylesheet
#
style() {
cat <<EOF
<style type="text/css">
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
padding: 50px 0 5px 50px;
background: #1f1f1f url(${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png);
font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.bm {
position: relative;
float: left;
margin: 15px;
padding: 1px;
opacity: 1;
border: 15px solid black;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-webkit-transition: -webkit-box-shadow 600ms;
-moz-transition: -webkit-box-shadow 600ms;
-webkit-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5);
-moz-box-shadow: 0 0 1px 0 #555, 0 0 10px rgba(0,0,0,.5);
overflow: hidden;
}
.bm:hover {
-webkit-box-shadow: 0 0 40px #1ab0ff
, 0 0 3px #06bdff
, 0 1px 1px #4ee2ff
, 0 1px 0 #fff;
-moz-box-shadow: 0 0 40px #1ab0ff
, 0 0 3px #06bdff
, 0 1px 1px #4ee2ff
, 0 1px 0 #fff;
}
.bm:hover img {
opacity: 1;
}
.bm img {
width: 200px;
height: 150px;
opacity: .5;
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-webkit-transition: opacity 200ms;
-moz-transition: opacity 200ms;
}
.bm p {
margin: 0;
padding: 10px;
width: 100%;
background: rgba(0,0,0,.75);
position: absolute;
left: 0;
color: white;
letter-spacing: 1px;
bottom: -50px;
-webkit-font-smoothing: antialiased;
-webkit-transition: bottom 200ms ease-in;
}
.bm:hover p {
bottom: 0;
}
</style>
EOF
}
#
# Generate bookmark screenshots
#
screenshot_take() {
if [ "${_BM_SCREENSHOT_GET,,}" = 'true' -o ${__p:=0} -eq 1 ]; then
[[ ! -d "${_BM_SCREENSHOT_DIRECTORY}" ]] && { mkdir -p "${_BM_SCREENSHOT_DIRECTORY}" || die "Can't create thumbnail directory." ; }
[[ ! -z "${_BM_CMD_PRE_CAPTURE}" ]] && { local _pre="${_BM_CMD_PRE_CAPTURE//\{FILE\}/${1}}"; _pre="${_pre//\{URL\}/${2}}"; ${_pre} ; }
local _cmd="${_BM_CMD_CAPTURE//\{FILE\}/${1}}"; _cmd="${_cmd//\{URL\}/${2}}";
local _args="${_BM_CMD_CAPTURE_ARGS//\{FILE\}/${1}}"; _args="${_args//\{URL\}/${2}}";
eval ${_cmd} ${_args}
[[ ! -z "${_BM_CMD_POST_CAPTURE}" ]] && { local _post="${_BM_CMD_POST_CAPTURE//\{FILE\}/${1}}"; _post="${_post//\{URL\}/${2}}"; ${_post} ; }
fi
}
picturize() {
if [ -z "${1:-}" ]; then
awk -F'|' '$0 !~ /^[ ]*#/ {print $1,$4;}' "${_BM_BOOKMARK_FILE}" | while read m u; do
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" || ${__F:=0} -eq 1 ]] && { screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" & }
[[ "${_BM_SCREENSHOT_WAIT,,}" = 'true' ]] && wait
done
else
local _lines="$(search "${@}")"
if [ "${_BM_ASK_BEFORE_OPEN,,}" = 'true' -a ${__O:=0} -eq 0 -a ${__Y:=0} -eq 0 ]; then
if [ $(wc -l <<< "${_lines}") -gt 1 ]; then
read -p"More than one URL found. Open all ? [Y/N] : " -n1 _answer
[[ "${_answer,,}" != 'y' ]] && die "\nUse -O to force the first URL or refine your search"
fi
fi
local _all=''
while read _nl; do
readLines <<< "${_nl}"
if [ "${_BM_OPEN_ALL,,}" = 'true' -o ${__Y:=0} -eq 1 ]; then
IFS='|' read m d a u b <<< "${_nl}"
screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" &
else
IFS='|' read m d a u b <<< "${_nl}"
screenshot_take "${_BM_SCREENSHOT_DIRECTORY}/${m}.png" "${u}" &
[[ "${_BM_OPEN_FIRST,,}" = 'true' || ${__O:=0} -eq 1 ]] && exit 0
fi
done <<< "$(echo -e "${_lines}")"
fi
}
bookmark_generator() {
[[ ! -f "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png" && -f "/usr/share/bm/black-Linen.png" ]] && cp "/usr/share/bm/black-Linen.png" "${_BM_SCREENSHOT_DIRECTORY}/black-Linen.png"
echo "<!DOCTYPE html><html><head><meta charset="UTF-8"><title>bm v${VERSION} : all your bookmarks</title>" > "${1:-${_BM_HTML_FILE}}"
style >> "${1:-${_BM_HTML_FILE}}"
echo "</head><body>" >> "${1:-${_BM_HTML_FILE}}"
# search "${2:-}" | while IFS='|' read m a u T t d; do
awk '$0 !~ /^[ ]*#/' "${_BM_BOOKMARK_FILE}" | while IFS='|' read m d a u T t; do
echo " <div class=bm>
<a href='${u}'><img src='${_BM_SCREENSHOT_DIRECTORY}/${m}.png' alt='${u}' /></a>
<p>${T}<br/><a href='${_BM_SCREENSHOT_DIRECTORY}/${m}.png'>View image</a></p>
</div>" >> "${1:-${_BM_HTML_FILE}}"
done
echo "</body></html>" >> "${1:-${_BM_HTML_FILE}}"
[[ ${__O:=0} -eq 1 ]] && "${_BM_CMD_OPEN}" "${1:-${_BM_HTML_FILE}}"
}
#
# die print message to stderr and exit
#
die() {
echo -e "${@}" >&2
exit 1
}
#
# checkBinaries check the script is able to run and give hints
#
checkBinaries() {
if [ ${#} -ne 0 ]; then
local cmdErr="${@}"
else
local cmdErr="sed awk date iconv cat curl ${_BM_CMD_OPEN} ${_BM_CMD_MD5} column"
fi
set ${cmdErr}
while [ ${#} -ne 0 ]; do
if ! command -v "${1:-}" &>/dev/null; then
echo "command not found: ${1}" >&2
local rc=1
fi
shift
done
[[ ${rc:=0} -ne 0 ]] && die "At least one command is missing. Please install it before using bm."
# Checking sed
local rc=1
[[ ! -f "/tmp/sedtest.$$" ]] && \
echo -n 'toto' > "/tmp/sedtest.$$" && \
sed -i.bak -e 's;^toto$;tata;' "/tmp/sedtest.$$" && \
[[ -f "/tmp/sedtest.$$.bak" ]] && \
grep -q "tata" "/tmp/sedtest.$$" && \
rc=0 && \
rm -f "/tmp/sedtest.$$" "/tmp/sedtest.$$.bak"
[[ ${rc} -ne 0 ]] && die "sed seems to not handle -i argument properly, please check"
}
#
# defineColors generate the variables to use to colorize the output
#
defineColors() {
export BLACK="\e[30m"; export BLACK_LIGHT="\e[90m"; export GRAY_DARK="${BLACK_LIGHT}"
export RED="\e[31m"; export RED_LIGHT="\e[91m"
export GREEN="\e[32m"; export GREEN_LIGHT="\e[92m"
export YELLOW="\e[33m"; export YELLOW_LIGHT="\e[93m"
export BLUE="\e[34m"; export BLUE_LIGHT="\e[94m"
export MAGENTA="\e[35m"; export MAGENTA_LIGHT="\e[95m"
export CYAN="\e[36m"; export CYAN_LIGHT="\e[96m"
export GRAY="\e[37m"; export GRAY_LIGHT="\e[97m"; export WHITE="${GRAY_LIGHT}"
export RESET="\e[0m"
export BOLD="\e[1m"; export BOLD_RESET="\e[21m"
export DIM="\e[2m"; export DIM_RESET="\e[22m"
export UNDERLINE="\e[4m"; export UNDERLINE_RESET="\e[24m"
export INVERT="\e[7m"; export INVERT_RESET="\e[27m"
if [ ! -z "${1:-}" ]; then
echo "Use the following colors to fit your needs :"
(
echo -e "${RESET}${BLACK}BLACK${RESET} - ${BLACK_LIGHT}BLACK_LIGHT${RESET} - ${BOLD}${BLACK}BOLD BLACK${RESET} - ${DIM}${BLACK}DIM BLACK${RESET}"
echo -e "${RESET}${RED}RED${RESET} - ${RED_LIGHT}RED_LIGHT${RESET} - ${BOLD}${RED}BOLD RED${RESET} - ${DIM}${RED}DIM RED${RESET}"
echo -e "${RESET}${GREEN}GREEN${RESET} - ${GREEN_LIGHT}GREEN_LIGHT${RESET} - ${BOLD}${GREEN}BOLD GREEN${RESET} - ${DIM}${GREEN}DIM GREEN${RESET}"
echo -e "${RESET}${YELLOW}YELLOW${RESET} - ${YELLOW_LIGHT}YELLOW_LIGHT${RESET} - ${BOLD}${YELLOW}BOLD YELLOW${RESET} - ${DIM}${YELLOW}DIM YELLOW${RESET}"
echo -e "${RESET}${BLUE}BLUE${RESET} - ${BLUE_LIGHT}BLUE_LIGHT${RESET} - ${BOLD}${BLUE}BOLD BLUE${RESET} - ${DIM}${BLUE}DIM BLUE${RESET}"
echo -e "${RESET}${MAGENTA}MAGENTA${RESET} - ${MAGENTA_LIGHT}MAGENTA_LIGHT${RESET} - ${BOLD}${MAGENTA}BOLD MAGENTA${RESET} - ${DIM}${MAGENTA}DIM MAGENTA${RESET}"
echo -e "${RESET}${CYAN}CYAN${RESET} - ${CYAN_LIGHT}CYAN_LIGHT${RESET} - ${BOLD}${CYAN}BOLD CYAN${RESET} - ${DIM}${CYAN}DIM CYAN${RESET}"
echo -e "${RESET}${GRAY}GRAY${RESET} - ${GRAY_LIGHT}GRAY_LIGHT${RESET} - ${BOLD}${GRAY}BOLD GRAY${RESET} - ${DIM}${GRAY}DIM GRAY${RESET}"
echo -e "${RESET}${GRAY_DARK}GRAY_DARK${RESET} - ${WHITE}WHITE${RESET}"
) | column -t
echo -e "You could also use the ${UNDERLINE}UNDERLINE, the ${INVERT}INVERT and the ${RESET}RESET variable. INVERT_RESET and UNDERLINE_RESET also available"
exit 0
fi
}
##############################################################################
# MAIN
##############################################################################
##################### TRANSITIONNAL ############################
_regex='^(help|version|tags|ls|list|search|open|add|view|stats|statistics|clear|colors)$'
if [[ ${1:-} =~ ${_regex} ]]; then
case "$1" in
tags) __L=1 ;;
ls|list) __l=1 ;;
search) __s=1; __search="${@:2}" ;;
open) __o=1; __open="${@:2}";;
add) __a=1; __url="${2:-}"; __T="${3:-}"; __t="${4:-}"; __A="${5:-}";;
view) __r=1; __search="${@:2}" ;;
stats|statistics) __S=1 ;;
clear) die "This now a deprecated feature. Use -d instead." ;;
colors) __C=1;;
help) usage 1;exit 0;;
version) echo -n "${0} v${VERSION}";exit 0;;
esac
else
# -c config file
# -C colors print ==> exit
# -b bookmark file
# -d delete
# -D deletefile if set override _BM_DELETE_TO_FILE to true
# -w working dir
# -a add url =/= -l, -s, -S, -O, -o, -x, -X, -g, -C
# -A Accelerator
# -T title
# -t tags
# -s search
# -S stats
# -l list all url
# -o open
# -O open first
# -x copy to clipboard
# -X copy first URL
# -h help
# -H more help
# -v version
# -V more version ?
# -g generate html file
# -G use this file
# -g -g generate 1 file per tags
# used : AaBbCcDdE-F-GgHh-i----Ll--NnOoPp-q-rSsTt--Vv-wXxY--z----------
# available : ---------e-f----I-JjKk--Mm------Q-R-----Uu--W----yZ-0123456789
while getopts ":a:A:b:c:d:G:o:P:q:r:s:t:T:w:x:BCDEFghHilLnNOpPSvVXYz" option; do
case ${option} in
a) __a=1; __url="${OPTARG}";; # Add url to bookmark
A) __A="${OPTARG}";; # AcceleratoR
b) __b="${OPTARG}";; # Bookmark file
B) __B=1;; # Don't load the default config file
c) __c="${OPTARG}";; # Config file
C) __C=1;; # Print color table
d) __d=1; __del="${OPTARG}";; # Config file
D) __D=1;; # Print color table
E) __E=1;; # Open the bm.bm file with the $EDITOR
F) __F=1;; # Force the add or delete or picture
g) (( __g++ ));; # generate html file(s)
G) __G=1; __file="${OPTARG}";; # Generate this file (only for g=1)
h|H) usage; exit 0;; # Help
i) __i=1;; # Ignore case when searching
l) __l=1;; # List all bookmarks
L) __L=1;; # List all tags
n) __n=1;; # sort by date
N) __N=1;; # sort by date (reverse)
o) __o=1; __open="${OPTARG}";; # Open
O) __O=1;; # Open First
p) __p=1;; # Take a screenshot
P) __P=1; __search="${OPTARG}";; # Take all screenshot
q) __q=1; __search="${OPTARG}";; # Search
r) __r=1; __search="${OPTARG}";; # Search
s) __s=1; __search="${OPTARG}";; # Search
S) __S=1;; # Print Statistics
t) __t="${OPTARG}";; # tags for a URL
T) __T="${OPTARG}";; # Title for a URL
v|V) echo -n "${0} v${VERSION}"; [[ "${option}" = 'V' ]] && echo -n " [commit: ${RELEASE}]"; echo ''; exit 0;;
w) __w="${OPTARG}";; # Working directory
x) __x=1; __copy="${OPTARG}";; # Copy
X) __X=1;; # Copy First
Y) __Y=1;; # Open/copy All
z) __z=1;; # Alternative print listing
:) echo "Missing argument for '-${OPTARG}'" >&2 ; exit 1 ;;
?) echo "Argument unknown '-${OPTARG}'" >&2 ; exit 1 ;; # usage;;
*) echo "Argument unknown '-${option}'" >&2 ; exit 1 ;; # usage;;
esac
done
fi
# Parsing args is done, Starting to work, so check if binaries are here
checkBinaries
# defineColors MUST be called before the config() or _BM_PRINT_LINE will be in trouble.
defineColors
# Before loading config, checking if working dir exist...
if [ ! -z "${__w:=}" -a ! -d "${__w}" ]; then
die "Working directory '${__w}' doesn't exist !" >&2
fi
# Loading config
config "${__c}"
[[ ! -z "${__b:=}" ]] && export _BM_BOOKMARK_FILE="${__b}"
# Following config, we maybe not have to check the capture tool
[[ ${_BM_CMD_CAPTURE_CHECK} ]] && checkBinaries "${_BM_CMD_CAPTURE}"
# If no bookmark file exist, create one if allowed
if [ ! -f "${_BM_BOOKMARK_FILE}" -a "${_BM_CREATE_BOOKMARK_FILE,,}" = 'true' ]; then
# Remember fields : 1=md5, 2=date, 2=accel, 3=url, 4=title, 5=tags
echo "eef521de8df447ad392dbace16cf2edc|$(date '+%FT%TZ')|:bm|https://github.com/The-Repo-Club/repomenu-extra/|Download link for repomenu-extra|default,shell,scripts" >> "${_BM_BOOKMARK_FILE}"
fi
# Starting to work with args. If none probably list...
[[ ${#} -eq 0 && "${_BM_NO_ARGS_FORCE_HELP,,}" = 'true' ]] && { usage ; exit; }
# Only one action at a time
if [ $(( ${__a:=0} + ${__C:=0} + ${__d:=0} + ${__E:=0} + ${__g:=0} + ${__l:=0} + ${__L:=0} + ${__o:=0} + ${__P:=0} + ${__q:=0} + ${__r:=0} + ${__s:=0} + ${__S:=0} + ${__x:=0} )) -gt 1 ]; then
echo "You have to choose between -a, -C, -d, -E, -g, -l, -L, -o, -P, -q, -r, -s, -S, -x" >&2
echo "Use -h for help" >&2
exit 0
fi
if [ $(( ${__O:=0} + ${__Y:=0} + ${__X:=0} )) -gt 1 ]; then
echo "You have to choose between -O, -X, -Y" >&2
echo "Use -h for help" >&2
exit 0
fi
# Executing actions
[[ ${__a} -eq 1 ]] && { saveUrl ; exit ; }
[[ ${__C} -eq 1 ]] && { defineColors 1 ; exit ; }
[[ ${__d} -eq 1 ]] && { delete_bookmark "${__del}"; exit ; }
[[ ${__g} -ge 1 ]] && { bookmark_generator "${__file:=}" ''; exit ; }
[[ ${__l} -eq 1 ]] && { search ; exit; }
[[ ${__L} -eq 1 ]] && { list_tags ; exit; }
[[ ${__o} -eq 1 ]] && { open_bookmark "${__open}" ; exit; }
[[ ${__P} -eq 1 ]] && { picturize "${__search}" ; exit; }
[[ ${__r} -eq 1 ]] && { recorded_picture "${__search}" ; exit; }
[[ ${__q} -eq 1 ]] && { download_title "${__search}" ; exit; }
[[ ${__s} -eq 1 ]] && { search_bookmarks "${__search}" ; exit; }
[[ ${__S} -eq 1 ]] && { stats ; exit; }
[[ ${__x} -eq 1 ]] && { copy_bookmark "${__copy}" ; exit; }
[[ ${__E} -eq 1 ]] && { edit_bookmark ; exit; }
search
# $Format:%cn @ %cD$ : $Id$