set -e
if (( BASH_VERSINFO[0] < 4 )) ; then
printf '(Bash version must be >=4)\n' >&2
exit 1
fi
declare -gr DATA_FILE="${XDG_DATA_HOME}/carlinigraphy/conf/data"
declare -gr DATA_DIR="${DATA_FILE%/*}"
mkdir -p "$DATA_DIR"
touch "$DATA_FILE"
declare -gA DB=()
declare -ga DB_ORDER=()
declare -g ENTRY
declare -gi _ENTRY_NUM=0
function usage {
local outfile
case "$1" in
0) outfile=/dev/stdout ;;
'') outfile=/dev/stdout ;;
*) outfile=/dev/stderr ;;
esac
cat <<EOF >"$outfile"
usage: ${BASH_SOURCE[0]##*/} command NICKNAME [param1..paramN]
commands:
cd \`cd\` to NICKNAME's in a subshell
clean \`rm\` all but last 5 database backups
db edit conf's db
edit ARGS edit NICKNAME w/ optional args
help CMD prints help text for CMD
list lists all nicknames in db
set PATH+ associates PATH(s) with a given NICKNAME
sort \`sort\` conf's db
EOF
exit "$1"
}
function new_entry {
local lineno="$1" nick="$2"
local entry="_ENTRY_$(( ++_ENTRY_NUM ))"
declare -gA "$entry"
local items="_ENTRY_ITEMS_${_ENTRY_NUM}"
declare -ga "$items"
local -n entry_r="$entry"
entry_r['items']="$items"
if [[ $lineno && $nick ]] ; then
DB["$nick"]="$entry"
DB_ORDER+=( "$nick" )
entry_r['lineno']="$lineno"
fi
declare -g ENTRY="$entry"
}
function db_store {
local -i n
local target
local base="${DATA_DIR}"/backup
while
target="${base}.~$(( ++n ))~" &&
[[ -f "$target" ]]
do :; done
mv "$DATA_FILE" "$target"
{ for nick in "${DB_ORDER[@]}" ; do
local -n entry_r="${DB[$nick]}"
local -n items_r="${entry_r['items']}"
printf '%s\n' "$nick"
printf ' %s\n' "${items_r[@]}"
printf '\n'
done } > "$DATA_FILE"
}
function db_load {
local -i lineno=0
while IFS=$'\n' read -r line ; do
(( ++lineno ))
is_whitespace "$line" && continue
is_comment "$line" && continue
if is_nick "$line" ; then
new_entry "$lineno" "${BASH_REMATCH[1]}"
continue
fi
if ! local -n entry_r="$ENTRY" 2>/dev/null ; then
printf '(Unexpected indent on line %d.)\n' "$lineno" >&2
exit 1
fi
local -n items_r="${entry_r['items']}"
items_r+=( "${line##* }" )
done < "$DATA_FILE"
}
function is_whitespace { [[ $1 =~ ^[[:space:]]*$ ]] ;}
function is_comment { [[ $1 =~ ^[[:space:]]*# ]] ;}
function is_nick { [[ $1 =~ ^([^[:space:]]+) ]] ;}
function db_validate {
local -i errors=0
local -a unfound=()
local nick
for nick in "${!DB[@]}" ; do
local -n entry_r="${DB[$nick]}"
local -n items_r="${entry_r['items']}"
local -a files
mapfile -t files < <(
xargs -I{} bash -c "printf '%s\n' {}" <<< "${items_r[@]}"
)
if [[ ! $files ]] ; then
printf 'Nickname [%s] has no associated paths.' "${nick}"
(( ++errors ))
fi
local f
for f in "${files[@]}" ; do
if [[ ! -f ${f} ]] &&
[[ ! -r ${f} ]] &&
[[ ! -L ${f} ]]
then
unfound+=( "$f" )
(( ++errors ))
fi
done
done
if (( ${#unfound[@]} )) ; then
{ printf '(Does not exist, or is not file:\n'
printf " '%s'\n" "${unfound[@]}"
printf ')\n'
} >&2
fi
if (( errors )) ; then
exit 1
fi
}
+-- 29 lines: ----------------------------------------------------------------------------
+-- 57 lines: ----------------------------------------------------------------------------
+-- 44 lines: ----------------------------------------------------------------------------
+-- 48 lines: ----------------------------------------------------------------------------
+-- 24 lines: ----------------------------------------------------------------------------
+-- 37 lines: ----------------------------------------------------------------------------
+-- 24 lines: ----------------------------------------------------------------------------
+-- 21 lines: ----------------------------------------------------------------------------
if (( ! $# )) ; then
usage 1
fi
declare -ga possible=()
declare -grA opts=(
['--help']=1
['help']=1
['edit']=1
['set']=1
['cd']=1
['sort']=1
['clean']=1
['list']=1
['fmt']=1
['db']=1
['.']=1
[',']=1
)
declare cmd="$1"
if [[ ! ${opts[$cmd]} ]] ; then
mapfile -t possible < <(
compgen -W "${!opts[*]}" -- "$1"
)
if (( ${#possible[@]} > 1 )) ; then
{ printf "(Ambiguous command \`%s' can be...\n" "$1"
printf ' %s\n' "${possible[@]}"
printf ')\n'
} >&2
exit 1
fi
cmd="${possible}"
fi
case "$cmd" in
-h | --help)
usage 0 ;;
'help') cmd_help "${@:2}" ;;
'set') cmd_set "${@:2}" ;;
'cd') cmd_cd "${@:2}" ;;
'sort') cmd_sort "${@:2}" ;;
'clean') cmd_clean "${@:2}" ;;
'db') cmd_db "${@:2}" ;;
'list') cmd_list "${@:2}" ;;
'edit') cmd_edit "${@:2}" ;;
'.') cmd_edit "${@:2}" ;;
',') cmd_cd "${@:2}" ;;
*) db_load
if [[ ! ${DB[$1]} ]] ; then
usage 1
else
cmd_edit "$@"
fi
;;
esac