#!/bin/bash
#   gast_symlink : create symbolic link to each wrapper script as alias name.
#
#   Copyright (C) 2010 Tadashi Koike
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program 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 General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
#   02110-1301, USA.
#
#----------------------------------------------------------------------
# (I am weak in English. Please modify English comment more suitable. :T.K)
#
ALIAS_FILE="gast.aliases"
CWD=$(pwd)
# get a directory path (full-path) of this script
SCRIPT_NAME=${0##*/}
if [[ "$0" =~ ".+/[^/]+" ]]; then
    SCRIPT_PATH=${0%/*}
else
    SCRIPT_PATH=${0%${SCRIPT_NAME}}
fi
case "${SCRIPT_PATH}" in
    '.' | '')   SCRIPT_PATH="${CWD}" ;;
    \./*)       SCRIPT_PATH="${CWD}/${SCRIPT_PATH#*/}" ;;
    '~')        SCRIPT_PATH="${HOME}" ;;
    \~/*)       SCRIPT_PATH="${HOME}/${SCRIPT_PATH#*/}" ;;
    \/*)                :       ;;
    *)          SCRIPT_PATH="${CWD}/${SCRIPT_PATH}" ;;
esac

### function declaration start ###
function create_symlink () {
    # $1 : symbolic link name
    # $2 : original file name
    if [ -z "$1" -a -z "$2" ]; then
        echo "${SCRIPT_NAME} : illegal file name. (symlink: '$1', original file: '$2')" >&2
        return
    fi
    local symlink_name=$1
    local exist_file=$2
    local symlink_to=

    if [ ! -e "${exist_file}" ]; then
        echo "${SCRIPT_NAME}: '${exist_file}' does not exists. Could not create symbolic link." >&2
        return
    fi

    if [ -e "${symlink_name}" -a  ! -h "${symlink_name}" ]; then
        echo "${SCRIPT_NAME}: '${symlink_name}' exists as normal file or directory. Could not create symbolic link." >&2
        return
    fi

    if [ -h "${symlink_name}" ]; then
        symlink_to=$(ls -l ${symlink_name})
        symlink_to="${symlink_to##* }"
        if [ "${symlink_to}" != "${exist_file}" ]; then
            echo "${SCRIPT_NAME}: ${symlink_name} exists for other file '${symlink_name}'" >&2
            return
        fi
    fi

    # Create symbolic link
    ln -sf "${exist_file}" "${symlink_name}"
    [ $? -ne 0 ] && echo "${SCRIPT_NAME}: error occured. Couldn't create symbolic link '${symlink_name}'" >&2
}

function delete_symlink () {
    # $1 : symbolic link name
    # $2 : original file name
    if [ -z "$1" ]; then
        echo "${SCRIPT_NAME} : illegal file name. (symlink:'$1')" >&2
        return
    fi
    local symlink_name=$1
    local original_name=$2
    local symlink_to=

    [ ! -e "${symlink_name}" ] && return

    # now, at least file exists.
    if [ ! -h "${symlink_name}" ]; then
        echo "${SCRIPT_NAME} : '${symlink_name}' exists as normal file or directory. Could not delete it." >&2
        return
    fi

    # now, ${symlink_name} is recognized as a symbolic link
    symlink_to=$(ls -l ${symlink_name})
    symlink_to="${symlink_to##* }"
    if [ -n "${original_name}" -a "${symlink_to}" != "${original_name}" ]; then
        echo "${SCRIPT_NAME} : stop deleting symbolic link '${symlink_name}'. It linked to '${symlink_to}'." >&2
        return
    fi

    # Delete symbolic link
    unlink "${symlink_name}"
    [ $? -ne 0 ] && echo "${SCRIPT_NAME}: Couldn't delete symbolic link '${symlink_name}'" >&2
}

### MAIN ###
unlink_flag=
while getopts 'hu' OPTION
do
    case "${OPTION}" in
    u)  unlink_flag=1
        ;;
    ?)  echo "Usage: ${SCRIPT_NAME} : [-u] [-h] [<script_name> [...]]" >&2
        echo " [OPTION]" >&2
        echo "  -u : delete symbolic link for <script_name>" >&2
        echo " [ARGUMENT]" >&2
        echo "  <script_name> : gast-family script file. if there is no argument," >&2
        echo "                  all gast-family scripts in ${ALIAS_FILE} are treated." >&2
        [ "${OPTION}" = "h" ] && exit 0
        exit 1
        ;;
    esac
done
shift $(($OPTIND - 1))

script_name_list=($@)
finished_list=()
argument_check=
[ $# -gt 0 ] && argument_check=yes

cd "${SCRIPT_PATH}"> /dev/null 2>&1

if [ ! -f "${ALIAS_FILE}" ]; then
    echo "${SCRIPT_NAME}: Couldn't find ${ALIAS_FILE}. exit." >&2
    exit 1
fi

line_no=0
while read -r line
do
    let line_no+=1
    [[ "${line}" =~ "^[:blank:]*$" ]] && continue
    [[ "${line}" =~ "^[:blank:]*#" ]] && continue
    if [[ "${line}" =~ "^[:blank:]*([^[:blank:]]+)[:blank:]*\:[:blank:]*([^#]+)[:blank:]*(#.*)*$" ]]; then
        :
    else
        echo "${SCRIPT_NAME}: format error in LINE ${line_no} of ${ALIAS_FILE}."
        continue
    fi
    gast_family_script=${BASH_REMATCH[1]}	# script name
    alias_list=${BASH_REMATCH[2]//,/ }		# list of alias name

    # if user specified script name(s) by command argument, treat only it.
    if [ -n "${argument_check}" ]; then
        hit_flag=
        cnt=0
        while [ -n "${script_name_list[$cnt]}" ]
        do
            if [ "${gast_family_script}" = "${script_name_list[$cnt]}" ]; then
                hit_flag=1
                break
            fi
            let cnt+=1
        done
        [ -z "${hit_flag}" ] && continue
    fi

    # Loop for create/delete symbolic link for ${gast_family_script}
    for alias_name in ${alias_list}
    do
        if [ -z "${unlink_flag}" ]; then
            create_symlink "${alias_name}" "${gast_family_script}"
        else
            delete_symlink "${alias_name}" "${gast_family_script}"
        fi
    done
    finished_list=(${finished_list[@]} ${gast_family_script})
done < "${ALIAS_FILE}"

# check
if [ -n "${argument_check}" ]; then
    cnt=0
    while [ -n "${script_name_list[$cnt]}" ]
    do
        for finished in ${finished_list[@]}
        do
            if [ "${script_name_list[$cnt]}" = "${finished}" ]; then
                unset script_name_list[$cnt]
                break
            fi
        done
        let cnt+=1
    done
    script_name_list=(${script_name_list[@]})
    if [ -n "${script_name_list[0]}" ]; then
        for script_name in ${script_name_list[@]}
        do
            warning_list="${warning_list}'${script_name}',"
        done
        warning_list="${warning_list%,}"
        echo "${SCRIPT_NAME}: there is no entry for ${warning_list}." >&2
    fi
fi
