diff options
Diffstat (limited to 'ctct.in')
-rw-r--r-- | ctct.in | 287 |
1 files changed, 181 insertions, 106 deletions
@@ -37,11 +37,14 @@ test -f "$user_config_file" \ ## CONSTANTS: exec_name="${0##*/}" -EXIT_SUCCESS=0 -EXIT_FAILURE=1 -EXIT_ERROR=2 -TRUE=0 -FALSE=1 +RET_SUCCESS=@ret_success@ +RET_FAILURE=@ret_failure@ +RET_BADSYNTAX=@ret_badsyntax@ +RET_ERROR=@ret_error@ + + +# Expand non matching globs to the empty string instead of themselves. +shopt -s nullglob function print_help() { @@ -59,101 +62,125 @@ usage: EOF } + function cleanup() { test -v tmp_file && test -f "$tmp_file" && rm -f "$tmp_file" } trap cleanup EXIT + function main() { + local ret + if ! test -d "$datadir" && ! mkdir "$datadir" then print_error "$exec_name: $datadir could not be created, quitting..." - exit 1 + return $RET_ERROR fi - test $# -eq 0 && print_help && exit 1 + test $# -eq 0 && print_help && return $RET_BADSYNTAX if [ "$1" = "-h" ] || ( [[ "--help" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then - print_help; exit 0 + print_help; return $RET_SUCCESS elif [[ "--version" =~ "$1" ]] && [ ${#1} -ge 3 ] then - print_version; exit 0 + print_version; return $RET_SUCCESS elif [ "$1" = "-l" ] || \ ( [[ "--list-all" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then - list_all; exit $? + list_all; return $? elif [ "$1" = "-s" ] || \ ( [[ "--search-by-name" =~ ^"$1" ]] && [ ${#1} -ge 13 ] ) then - shift; find_similar "Found:" "$@"; exit $? + shift; search_by_name "Found:" "$@"; return $? elif [ "$1" = "-S" ] || \ ( [[ "--search-by-data" =~ ^"$1" ]] && [ ${#1} -ge 13 ] ) then - shift; search_file "Found:" "$@"; exit $? + shift; search_by_content "Found:" "$@"; return $? elif [ "$1" = "-e" ] || ( [[ "--edit" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then test $# -lt 2 && print_error "$exec_name: no entry specified." \ - && exit 1 - edit_file "$2"; exit $? + && return $RET_BADSYNTAX + edit_contact "$2"; return $? elif [ "$1" = "-d" ] || \ ( [[ "--delete" =~ ^"$1" ]] && [ ${#1} -ge 3 ] ) then test $# -lt 2 && print_error "$exec_name: no entry to be deleted." \ - && exit 1 - shift; delete_file "$@"; exit $? + && return $RET_BADSYNTAX + shift; delete_file "$@"; return $? elif [[ "--rename" =~ ^"$1" ]] && [ ${#1} -ge 3 ] then ( test $# -lt 2 && print_error "$exec_name: no entry specified." ) || \ ( test $# -lt 3 && print_error "$exec_name: no new name specified." ) \ - && exit 1 - rename_file "$2" "$3"; exit $? + && return $RET_BADSYNTAX + rename_contact "$2" "$3"; return $? elif [[ "$1" == '--' ]] then shift 1 - test $# -eq 0 && print_help && exit 1 + test $# -eq 0 && print_help && return $RET_BADSYNTAX elif [[ "$1" =~ ^- ]] then - print_error "$exec_name: unknown option '$1'"; exit 1 + print_error "$exec_name: unknown option '$1'"; return $RET_BADSYNTAX fi - if ! find_exact "$1" + display_exact "$1" + ret=$? + if [ $ret -ne $RET_SUCCESS ] then - if ! find_similar "Did you mean:" "$@" + if [ $ret -eq $RET_BADSYNTAX ] + then + return $RET_BADSYNTAX + fi + + if ! search_by_name "Did you mean:" "$@" then print_msg "No match found." fi - return 1 + return $RET_FAILURE fi + + return $RET_SUCCESS } + +# $1: message function print_msg() { printf "%s\n" "$@" } + +# $1: error message function print_error() { printf "%s\n" "$@" >&2 } -function find_exact() + +# $1: contact name +function display_exact() { local file - file="$datadir/$(get_filename "$1")" || return $FALSE + + check_syntax "$1" || return $RET_BADSYNTAX + file="$datadir/$(get_filename "$1")" || return $RET_FAILURE if test "$visual_program" = "cat" then - $output_program < "$file" + $output_program < "$file" || return $RET_ERROR else - $output_program < "$file" | $visual_program + $output_program < "$file" | $visual_program || return $RET_ERROR fi - return $TRUE + + return $RET_SUCCESS } -# $1: initial success message; $2-${$#}: search-patterns -function find_similar() + +# $1: initial success message +# ${@:2}: search-patterns +function search_by_name() { local found msg name bool file pattern @@ -167,15 +194,11 @@ function find_similar() # else the reverse order (last.first) would have to be checked as well if [[ "$*" =~ \. ]] then - return $FALSE + return $RET_FAILURE fi - - for file in "$datadir"/* - do - # Ignore non-regular files. - test -f "$file" || continue - name="${file##*/}" + for name in $(list_all || return $RET_ERROR) + do bool=true for pattern in "${@,,}" do @@ -191,60 +214,71 @@ function find_similar() print_msg " $name" fi done - $found && return $TRUE || return $FALSE + + $found && return $RET_SUCCESS || return $RET_FAILURE } -function search_file() + +# $1: initial success message +# ${@:2}: regular expressions +function search_by_content() { - local found=false msg="$1" valid file pattern - shift 1 # skip $1 - - for file in "$datadir"/* - do - # Ignore non-regular files. - test -f "$file" || continue + local msg files regexp ret - valid=true - if test "$output_program" = "cat" - then - for pattern in "$@" - do - ! grep -qEi "$pattern" "$file" && valid=false && break - done - else - for pattern in "$@" - do - ! $output_program < "$file" | \ - grep -qEi "$pattern" "$file" && valid=false && break - done - fi - if $valid - then - ! $found && print_msg "$msg" && found=true - print_msg " ${file##*/}" - fi + msg="$1" + shift 1 + + # Change to datadir to be able to treat file names and contact names + # equally. + cd "$datadir" + files=( $(list_all || return $RET_ERROR) ) + cd - + + for regexp in "${@,,}" + do + files=( $(grep -Eil "$regexp" ${files[@]}) ) + ret=$? + [ $ret -eq 1 ] && return $RET_FAILURE + [ $ret -gt 1 ] && return $RET_ERROR done - $found && return $TRUE || return $FALSE + + print_msg "$msg" + printf " %s\n" ${files[@]} + return $RET_SUCCESS } + +# $1: [full_path] function list_all() { - # Use find instead of ls to avoid listing non-regular files. - find "$datadir" -maxdepth 1 -type f | sed 's|.*/||' + local fmt + [[ "$1" == 'full_path' ]] && fmt='%p\n' || fmt='%f\n' + + # Only list regular files with valid names. + find "$datadir" -maxdepth 1 -type f -regextype posix-extended -regex \ + '.*/[[:alpha:]]+([-_][[:alpha:]]+)*(\.[[:alpha:]]+([-_][[:alpha:]]+)*)?' \ + -printf "$fmt" + + [ $? -eq 0 ] && return $RET_SUCCESS || return $RET_ERROR } -function edit_file() + +# $1: contact name +function edit_contact() { - local file editor new=false + local file editor new + check_syntax "$1" || return $RET_BADSYNTAX + + new=false if ! file="$datadir/$(get_filename "$1")" then - if check_syntax "$1" && check_non_existance "$1" + if check_non_existance "$1" then file="$datadir/$1" new=true else - return $FALSE + return $RET_FAILURE fi fi @@ -260,41 +294,60 @@ function edit_file() if test "$input_program" = "cat" -a "$output_program" = "cat" then - $new && touch "$file" # vim does not save an empty file - $editor "$file" + if $new + then + # vim does not save an empty file. + touch "$file" || return $RET_ERROR + fi + + $editor "$file" || return $RET_ERROR else - tmp_file="$(mktemp)" - chmod 600 "$tmp_file" - ! $new && $output_program < "$file" > "$tmp_file" - $editor "$tmp_file" - eval $input_program < "$tmp_file" > "$file" - rm -f "$tmp_file" && unset tmp_file + tmp_file="$(mktemp || return $RET_ERROR)" + chmod 600 "$tmp_file" || return $RET_ERROR + + if ! $new + then + $output_program < "$file" > "$tmp_file" || return $RET_ERROR + fi + + $editor "$tmp_file" || return $RET_ERROR + $input_program < "$tmp_file" > "$file" || return $RET_ERROR + rm -f "$tmp_file" && unset tmp_file || return $RET_ERROR fi + + return $RET_SUCCESS } -function rename_file() -{ - check_syntax "$2" || return $FALSE +# $1: old name +# $2: new name +function rename_contact() +{ local file + + check_syntax "$1" || return $RET_BADSYNTAX + check_syntax "$2" || return $RET_BADSYNTAX + if file="$datadir/$(get_filename "$1")" then if get_filename "$2" > /dev/null then print_error "$exec_name: Entry \"$2\" does already exist." - return $FALSE + return $RET_FAILURE elif ! check_non_existance "$2" then - return $FALSE + return $RET_FAILURE else - mv "$file" "$datadir/$2" + mv "$file" "$datadir/$2" || return $RET_ERROR fi else print_error "$exec_name: Entry \"$1\" does not exist." - return $FALSE + return $RET_FAILURE fi } + +# $@: names of contacts to be deleted function delete_file() { local name file str @@ -304,17 +357,20 @@ function delete_file() # check for existance of all to be deleted files for name in "$@" do + check_syntax "$@" || return $RET_BADSYNTAX + if file="$(get_filename "$name")" then files[i++]="$file" else print_error "$exec_name: Contact \"$1\" does not exist, aborting..." - return $FALSE + return $RET_FAILURE fi done - if "$confirm_deletion"; then - # prepare confirmation + if $confirm_deletion + then + # Prepare confirmation. if test $# -eq 1; then str="Do you really want to delete the entry ${files[0]}" else @@ -325,19 +381,25 @@ function delete_file() str="Are you sure to delete all these entries" fi - # confirm and delete + # Confirm and delete. if confirm "$str"; then for file in "${files[@]}"; do - rm "$datadir/$file" + rm "$datadir/$file" || return $RET_ERROR done fi else # simply delete for file in "${files[@]}"; do - rm "$datadir/$file" + rm "$datadir/$file" || return $RET_ERROR done fi + + return $RET_SUCCESS } + +# For dot separated names (both parts interchangeable), get the permutation +# on disk (e.g. last.first -> first.last). +# $1: contact name function get_filename() { local rev @@ -351,13 +413,16 @@ function get_filename() if test -f "$datadir/$rev"; then printf "%s\n" "$rev" else - return $FALSE + return $RET_FAILURE fi else - return $FALSE + return $RET_FAILURE fi + + return $RET_SUCCESS } + # $1: confirmation string function confirm() { @@ -371,40 +436,50 @@ function confirm() read var if test -z "$var"; then - $confirm_default_yes && return $TRUE || return $FALSE + $confirm_default_yes && return $RET_SUCCESS || return $RET_FAILURE fi var="${var,,}" if test "$var" = "y" -o "$var" = "yes"; then - return $TRUE + return $RET_SUCCESS else - return $FALSE + return $RET_FAILURE fi } + +# $1: filename to be checked +# $2: [silent] function check_syntax() { - if [[ "$1" =~ ^[A-Za-z]+([-_][A-Za-z]+)*(\.[A-Za-z]+([-_][A-Za-z]+)*)?$ ]] + if [[ "$1" =~ \ + ^[[:alpha:]]+([-_][[:alpha:]]+)*(\.[[:alpha:]]+([-_][[:alpha:]]+)*)?$ ]] then - return $TRUE + return $RET_SUCCESS else - print_error "$exec_name: invalid name \"$1\"" \ - " An entry name may only contain letters," \ - " '-' and '_' as separators and exactly one dot ('.')" - return $FALSE + if [[ "$2" != 'silent' ]] + then + print_error "$exec_name: invalid name \"$1\"" \ + " An entry name may only contain letters," \ + " '-' and '_' as separators and exactly one dot ('.')" + fi + + return $RET_FAILURE fi } + # Only use this when $1 is assured not to be a regular file. +# $1: filename (relative to datadir) function check_non_existance() { if test -e "$datadir/$1" then print_error "$exec_name: file \"$1\" exists in $datadir," \ "however is not a regular file." - return $FALSE + return $RET_FAILURE else - return $TRUE + return $RET_SUCCESS fi } |