#!/bin/sh

set -e

PROG_NAME=$0
CTAGS_EXE=ctags
CTAGS_ARGS=
CTAGS_OPT_FILE=
TAGS_FILE=tags
PROJECT_ROOT=
LOG_FILE=
FILE_LIST_CMD=
FILE_LIST_CMD_IS_ABSOLUTE=0
UPDATED_SOURCE=
POST_PROCESS_CMD=
PAUSE_BEFORE_EXIT=0


ShowUsage() {
    echo "Usage:"
    echo "    $PROG_NAME <options>"
    echo ""
    echo "    -e [exe=ctags]: The ctags executable to run"
    echo "    -t [file=tags]: The path to the ctags file to update"
    echo "    -p [dir=]:      The path to the project root"
    echo "    -l [file=]:     The path to a log file"
    echo "    -L [cmd=]:      The file list command to run"
    echo "    -A:             Specifies that the file list command returns "
    echo "                    absolute paths"
    echo "    -s [file=]:     The path to the source file that needs updating"
    echo "    -x [pattern=]:  A pattern of files to exclude"
    echo "    -o [options=]:  An options file to read additional options from"
    echo "    -O [params=]:   Parameters to pass to ctags"
    echo "    -P [cmd=]:      Post process command to run on the tags file"
    echo "    -c:             Ask for confirmation before exiting"
    echo ""
}

while getopts "h?e:x:t:p:l:L:s:o:O:P:cA" opt; do
    case $opt in
        h|\?)
            ShowUsage
            exit 0
            ;;
        e)
            CTAGS_EXE="$OPTARG"
            ;;
        x)
            CTAGS_ARGS="$CTAGS_ARGS --exclude=$OPTARG"
            ;;
        t)
            TAGS_FILE="$OPTARG"
            ;;
        p)
            PROJECT_ROOT="$OPTARG"
            ;;
        l)
            LOG_FILE="$OPTARG"
            ;;
        L)
            FILE_LIST_CMD="$OPTARG"
            ;;
        A)
            FILE_LIST_CMD_IS_ABSOLUTE=1
            ;;
        s)
            UPDATED_SOURCE="$OPTARG"
            ;;
        c)
            PAUSE_BEFORE_EXIT=1
            ;;
        o)
            CTAGS_OPT_FILE="--options=$OPTARG"
            ;;
        O)
            CTAGS_ARGS="$CTAGS_ARGS $OPTARG"
            ;;
        P)
            POST_PROCESS_CMD="$OPTARG"
            ;;
    esac
done

shift $((OPTIND - 1))

if [ "$1" != "" ]; then
    echo "Invalid Argument: $1"
    exit 1
fi

echo "Locking tags file..."
echo $$ > "$TAGS_FILE.lock"

# Remove lock and temp file if script is stopped unexpectedly.
trap 'errorcode=$?; rm -f "$TAGS_FILE.lock" "$TAGS_FILE.files" "$TAGS_FILE.temp"; exit $errorcode' INT QUIT TERM EXIT

INDEX_WHOLE_PROJECT=1
if [ -f "$TAGS_FILE" ]; then
    if [ "$UPDATED_SOURCE" != "" ]; then
        echo "Removing references to: $UPDATED_SOURCE"
        tab="	"
        cmd="grep --text -Ev '^[^$tab]+$tab$UPDATED_SOURCE$tab' '$TAGS_FILE' > '$TAGS_FILE.temp'"
        echo "$cmd"
        eval "$cmd" || true
        INDEX_WHOLE_PROJECT=0
    fi
fi

if [ $INDEX_WHOLE_PROJECT -eq 1 ]; then
    if [ -n "${FILE_LIST_CMD}" ]; then
        if [ "${PROJECT_ROOT}" = "." ] || [ $FILE_LIST_CMD_IS_ABSOLUTE -eq 1 ]; then
            echo "Running file list command"
            echo "eval $FILE_LIST_CMD > \"${TAGS_FILE}.files\""
            eval "$FILE_LIST_CMD" > "${TAGS_FILE}.files"
        else
            # If using a tags cache directory, use absolute paths
            echo "Running file list command, patching for absolute paths"
            echo "eval $FILE_LIST_CMD"
            eval "$FILE_LIST_CMD" | while read -r l; do
                echo "${PROJECT_ROOT%/}/${l}"
            done > "${TAGS_FILE}.files"
        fi
        CTAGS_ARGS="${CTAGS_ARGS} -L"
        CTAGS_ARG_LAST="${TAGS_FILE}.files"
    else
        CTAGS_ARG_LAST="${PROJECT_ROOT}"
    fi

    echo "Running ctags on whole project"
    if [ "$CTAGS_OPT_FILE" != "" ]; then
        echo "$CTAGS_EXE -f \"$TAGS_FILE.temp\" \"$CTAGS_OPT_FILE\" $CTAGS_ARGS \"$CTAGS_ARG_LAST\""
        "$CTAGS_EXE" -f "$TAGS_FILE.temp" "$CTAGS_OPT_FILE" $CTAGS_ARGS "$CTAGS_ARG_LAST"
    else
        echo "$CTAGS_EXE -f \"$TAGS_FILE.temp\" $CTAGS_ARGS \"$CTAGS_ARG_LAST\""
        "$CTAGS_EXE" -f "$TAGS_FILE.temp" $CTAGS_ARGS "$CTAGS_ARG_LAST"
    fi
else
    echo "Running ctags on \"$UPDATED_SOURCE\""
    if [ "$CTAGS_OPT_FILE" != "" ]; then
        echo "$CTAGS_EXE -f \"$TAGS_FILE.temp\" \"$CTAGS_OPT_FILE\" $CTAGS_ARGS --append \"$UPDATED_SOURCE\""
        "$CTAGS_EXE" -f "$TAGS_FILE.temp" "$CTAGS_OPT_FILE" $CTAGS_ARGS --append "$UPDATED_SOURCE"
    else
        echo "$CTAGS_EXE -f \"$TAGS_FILE.temp\" $CTAGS_ARGS --append \"$UPDATED_SOURCE\""
        "$CTAGS_EXE" -f "$TAGS_FILE.temp" $CTAGS_ARGS --append "$UPDATED_SOURCE"
    fi
fi

if [ "$POST_PROCESS_CMD" != "" ]; then
    echo "Running post process"
    echo "$POST_PROCESS_CMD \"$TAGS_FILE.temp\""
    $POST_PROCESS_CMD "$TAGS_FILE.temp"
fi

echo "Replacing tags file"
echo "mv -f \"$TAGS_FILE.temp\" \"$TAGS_FILE\""
if [ -f "$TAGS_FILE.temp" ]; then
    mv -f "$TAGS_FILE.temp" "$TAGS_FILE"
fi

echo "Unlocking tags file..."
rm -f "$TAGS_FILE.lock"

echo "Done."

if [ $PAUSE_BEFORE_EXIT -eq 1 ]; then
    printf "Press ENTER to exit..."
    read -r
fi
