aboutsummaryrefslogtreecommitdiff
path: root/ctct.in
diff options
context:
space:
mode:
Diffstat (limited to 'ctct.in')
-rw-r--r--ctct.in287
1 files changed, 181 insertions, 106 deletions
diff --git a/ctct.in b/ctct.in
index a705e59..74a40f8 100644
--- a/ctct.in
+++ b/ctct.in
@@ -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
}