#!/bin/bash # # ctct - a simple console contact manager # # Copyright 2015, 2016 Einhard Leichtfuß # # ctct is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # ctct is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with ctct. If not, see . # ## DEFAULT SETTINGS: system_config_dir="@default_confdir@" user_config_dir="@default_user_config_dir@" datadir="@default_datadir@" fallback_editor="@default_fallback_editor@" default_editor= # none - use $EDITOR input_program="@default_input_program@" output_program="@default_output_program@" visual_program="@default_visual_program@" confirm_deletion=@default_confirm_deletion@ confirm_default_yes=@default_confirm_default_yes@ ## USER SETTINGS: test -f "$system_config_dir/config.sh" \ && source "$system_config_dir/config.sh" test -f "$user_config_dir/config.sh" \ && source "$user_config_dir/config.sh" ## CONSTANTS: exec_name="${0##*/}" TRUE=0 FALSE=1 function print_help() { cat << EOF usage: $exec_name - show contact $exec_name -l - list all entries $exec_name [-s] [...] - search by name $exec_name -S [...] - search by data $exec_name -e - edit / create entry $exec_name -d [...] - delete entries $exec_name --rename - rename entry $exec_name --version - show version information $exec_name -h - print this help EOF } function main() { if ! test -d "$datadir" && ! mkdir "$datadir" then print_error "$exec_name: $datadir could not be created, quitting..." exit 1 fi test $# -eq 0 && print_help && exit 1 if [ "$1" = "-h" ] || ( [[ "--help" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then print_help; exit 0 elif [[ "--version" =~ "$1" ]] && [ ${#1} -ge 3 ] then print_version; exit 0 elif [ "$1" = "-l" ] || \ ( [[ "--list-all" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then list_all; exit $? elif [ "$1" = "-s" ] || \ ( [[ "--search-by-name" =~ ^"$1" ]] && [ ${#1} -ge 13 ] ) then shift; find_similar "Found:" "$@"; exit $? elif [ "$1" = "-S" ] || \ ( [[ "--search-by-data" =~ ^"$1" ]] && [ ${#1} -ge 13 ] ) then shift; search_file "Found:" "$@"; exit $? elif [ "$1" = "-e" ] || ( [[ "--edit" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ); then edit_file "$2"; exit $? elif [ "$1" = "-d" ] || \ ( [[ "--delete" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then test $# -lt 2 && print_error "$exec_name: no contact to be deleted" \ && exit 1 shift; delete_file "$@"; exit $? elif [[ "--rename" =~ ^"$1" ]] && [ ${#1} -ge 3 ] then rename_file "$2" "$3"; exit $? elif [[ "$1" =~ ^- ]]; then print_error "$exec_name: unknown option $1"; exit 1 fi if ! find_exact "$1" then find_similar "Did you mean:" "$@" || print_msg "No match found." fi } function print_msg() { printf "%s\n" "$*" } function print_error() { printf "%s\n" "$*" >&2 } function find_exact() { local file file="$datadir/$(get_filename "$1")" || return $FALSE if test "$visual_program" = "cat" then "$output_program" < "$file" else "$output_program" < "$file" | "$visual_program" fi return $TRUE } # $1: initial success message; $2-${$#}: search-patterns function find_similar() { local found=false msg="$1" name bool file pattern shift 1 # skip $1 # disallow '.' in any pattern # - any pattern should be either part of the first or the last name # else the reverse order (last.first) would have to be checked as well if [[ "$*" =~ \. ]] then return $FALSE fi for file in "$datadir"/* do name="${file##*/}" bool=true for pattern in "${@,,}" do if ! [[ "${name,,}" =~ "$pattern" ]] then bool=false break fi done if $bool then ! $found && print_msg "$msg" && found=true print_msg " $name" fi done $found && return $TRUE || return $FALSE } function search_file() { local found=false msg="$1" valid file pattern shift 1 # skip $1 for file in "$datadir"/* do valid=true if test "$output_program" = "cat" then for pattern in "$@" do ! grep -qi "$pattern" "$file" && valid=false && break done else for pattern in "$@" do ! "$output_program" < "$file" | \ grep -qi "$pattern" "$file" && valid=false && break done fi if $valid then ! $found && print_msg "$msg" && found=true print_msg " ${file##*/}" fi done $found && return $TRUE || return $FALSE } function list_all() { ls -1 "$datadir" } function edit_file() { local file tmp_file editor if ! [[ "$1" =~ ^[A-Za-z]+([-_][A-Za-z]+)*(\.[A-Za-z]+([-_][A-Za-z]+)*)? ]] then print_error "$exec_name: An entry may only contain letters" \ "- of the english alphabet and" \ "- '-' and '_' as separators and exactly one dot ('.')" return $FALSE fi if ! file="$datadir/$(get_filename "$1")" then print_error "$exec_name: Contact \"$1\" does not exist." return $FALSE fi if test -n "$default_editor" then editor="$default_editor" elif test -n "$EDITOR" && which "$EDITOR" >/dev/null 2>&1 then editor="$EDITOR" else editor="$fallback_editor" fi if test "$input_program" = "cat" -a "$output_program" = "cat" then "$editor" "$file" else tmp_file="$(mktemp)" "$output_program" < "$file" > "$tmp_file" "$editor" "$tmp_file" "$input_program" < "$tmp_file" > "$file" fi } function rename_file() { local file if file="$datadir/$(get_filename "$1")" then if get_filename "$2" > /dev/null then print_error "$exec_name: Entry \"$2\" does already exist." else mv "$file" "$datadir/$2" fi else print_error "$exec_name: Entry \"$1\" does not exist." return 1 fi } function delete_file() { local i files name file str typeset -i i=0 typeset -a files # check for existance of all to be deleted files for name in "$@" do if file="$(get_filename "$name")" then files[i++]="$file" else print_error "$exec_name: Contact \"$file\" does not exist, aborting..." return 1 fi done if "$confirm_deletion"; then # prepare confirmation if test $# -eq 1; then str="Do you really want to delete the entry ${files[0]}" else print_msg "Entries to be deleted:" for file in "${files[@]}"; do print_msg " $file" done str="Are you sure to delete all these entries" fi # confirm and delete if confirm "$str"; then for file in "${files[@]}"; do rm "$datadir/$file" done fi else # simply delete for file in "${files[@]}"; do rm "$datadir/$file" done fi } function get_filename() { local rev if test -f "$datadir/$1"; then printf "%s\n" "$1" elif [[ "$1" =~ \. ]] then rev=( ${1/\./ } ) rev="${rev[1]}.${rev[0]}" if test -f "$datadir/$rev"; then printf "%s\n" "$rev" else return $FALSE fi else return $FALSE fi } # $1: confirmation string function confirm() { local str="$1 " var if $confirm_default_yes; then str+="[Y/n]? " else str+="[y/N]? " fi printf "%s" "$str" read var if test -z "$var"; then $confirm_default_yes && return $TRUE || return $FALSE fi var="${var,,}" if test "$var" = "y" -o "$var" = "yes"; then return $TRUE else return $FALSE fi } function print_version() { cat << EOF ctct - Version @PACKAGE_VERSION@ Copyright 2015, 2016 Einhard Leichtfuß. License AGPLv3+: GNU Affero General Public License, version 3 or later - This is free software: You are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. EOF } main "$@" # vim: ts=2 sw=2