#! /usr/bin/zsh -feu

# I run this script to RSYNC the source tree faithfully from my
# (at the moment remote) workstation to BUILD server (localhost).

# The tree is in a SOURCE_REPO, and is synced to some ROOT directory
# on the localhost.

# The config files itself are expected inside ROOT, so rsync can update them.
# hopefully not overwrite while it's executing.

# those are:  .rsync.env

# see example
# to avoid copying cscope* files, and also the git files -- not relevant
# for building.

# Since the source tree on the build server contans (unfortunately)
# the build results, I make the rsync ignore those too.

# there are 2 grades of ignoring files:
# 1/ make sure it's not on the build host -- actively remove it
# 2/ ignore it. -- this has the annoying effect of reporting parent
#   dirs as non-removable as non-empty.


# For the build to succeed -- to nont consult the version control for example,
# I need some more fake files. Created them once:

# mkdir .repo;
# mkdir kernel/.git;

# I could create a "dont-delete" file inside them...
# ... as I use   "dont-delete" files to keep directories present.



# how to bootstrap:
# on server:
#    mkdir DIR2
#    mkdir .repo;  touch .repo/dont-delete
#    mkdir kernel/.git; touch kernel/.git/dont-delete

# on workstation:
#    scp  DIR/rsync*  server:DIR2/
# then on the server:
#    DIR2/rsync-from-me


# How to invoke:
# invoked in CWD -> these files are located:
# .rsync.env  and .rsync.ignore
# If not found -> exit.
# then parameters are ... the subdirs.

die()
{
    echo $@ >&2
    usage >&2
    exit 1;
}

do_rsync()
{
    # if [ $ECHO_RSYNC_COMMAND = y ]; then set -x;fi
    set -x
    local cmd=""
    # echo

    # fixme: choose the local from $ROOT or $SOURCE_REPO
    time $cmd rsync \
         $GLOBAL_OPTIONS \
         $FAITHFUL_COPY_OPTIONS \
         $COMPARISON_OPTIONS \
         $TRANSFER_OPTIONS \
         $RSYNC_RULES \
         "$@"
    # fixme: do I really want --archive?
    #  --exclude-from=./.rsync-ignore  --archive

    if [ $ECHO_RSYNC_COMMAND = y ]; then set +x;fi
}

unsetopt FUNCTION_ARGZERO
usage()
{
    cat <<EOF
usage: ${0##*/} [+-i] [--]  dir...
-i --hide-progress   don't show per-file progress-bar
-d --delete          delete also
-s --size-only       look at size, not time. (fixme: or --content?)
-v --verbose
-n --dry
-c --checksum        check by checksum, don't care about modification times.
-s --size-only       don't care about modification times.
EOF
}


if getopt -T; then # should test (( $? = -4 ))
    die "incompatible  getopt(1) installed. Abort"
fi



if ! [ -e .rsync.env ]; then
    die "missing .rsync.env file in $(pwd)"
fi

source .rsync.env

# sanity check:
if [ -z "${SOURCE_REPO-}" ]; then
    die "the env file does not define the SOURCE_REPO"
fi
if [ -z "${RSYNC_RULES-}" ]; then
    die "the env file does not define the RSYNC_RULES"
fi


ECHO_RSYNC_COMMAND=n

LOG_OPTIONS=()
LOG_OPTIONS_VERBOSE=(--itemize-changes --info=NAME,stats2 --human-readable)

TRANSFER_OPTIONS=(--partial)
# too slow: --bwlimit=100

delete_level=0

# --inplace
# --archive =  -rlptgoD
# ==
# --recursive --links --perms --group --owner --devices --specials
# --times

GLOBAL_OPTIONS=(--hard-links --omit-dir-times  --recursive --links  --devices --specials)
# I don't want to keep the --perms
FAITHFUL_COPY_OPTIONS=(--group --owner) # --times --perms
# I don't want --times, I want to mark it with current time, to work with make(1).

# By default we compare by time & thus maintain it too:
# log:  t with --times
#       T  ... current time!
# --checksum > --size-only > --ignore-times (basically no heuristics)
COMPARISON_OPTIONS=()
comparison_level=0

# fixme: non-implemented!
interactive="yes"

# todo: delete=no
# no,limit,yes
cmd_options=$(getopt --name $0 --shell bash \
              --longoptions hide-progress,progress,verbose,dry,size-only,checksum,delete,source:,backup: \
              --options ipvnfscdb: -- "$@") || { usage && exit 1 }
eval set -- "$cmd_options"

ROOT=$(pwd)

while (( $# > 0 )) do
    case $1 in
        #process_rest "$@"
        -b | --backup)
            GLOBAL_OPTIONS+=(--backup-dir $2)
            shift
            ;;
        -i | --hide-progress)
            # fixme!
            interactive=no
            ;;
        -p | --progress)
            GLOBAL_OPTIONS+=(--progress)
            # -v overrides this! fixme!
            ;;
        -v | --verbose ) # verbose
            LOG_OPTIONS=($LOG_OPTIONS_VERBOSE)
            ECHO_RSYNC_COMMAND=y
            ;;
        -n| --dry)
            GLOBAL_OPTIONS+=(--dry-run)
            LOG_OPTIONS=($LOG_OPTIONS_VERBOSE)
            ;;
        -d | --delete )
            ((delete_level+=1))
            ;;

        -s | --size-only) # size equals -> ok, unless force checksum.
            # todo: contradicts --checksum
            # --ignore-times --> don't check time.
            # --size-only --> look at size and decide.  (unless checksum forced.)
            # contradicst --checksum, --size-only !
            COMPARISON_OPTIONS=(--size-only)

            if [[ $comparison_level -gt 0 ]];
            then
                echo "mismatch in the heuristics"
                exit 1
            fi
            comparison_level=1
            ;;
        -c | --checksum)
            # otherwise:
            # todo: warn about conflict with -s
            COMPARISON_OPTIONS=(--checksum)
            if [[ $comparison_level -gt 0 ]];
            then
                echo "mismatch in the heuristics"
                exit 1
            fi
            comparison_level=2
            ;;
        -f)
            GLOBAL_OPTIONS+=($TRANSFER_OPTIONS)
            ;;
        --)
            shift
            break
            ;;
        --source )
            SOURCE_REPO=$2
            shift
            ;;
        *)
            echo $1>&2
            usage
            exit 2
    esac
    shift
done


typeset -a DELETE_OPTIONS
DELETE_OPTIONS=()

if [ $delete_level -gt 0 ]; then
    DELETE_OPTIONS=(--delete --delete-excluded)

    if [ $delete_level -gt 1 ]; then
        # DELETE_OPTIONS+=($DELETE_OPTIONS_UNSAFE)
    else
        DELETE_OPTIONS+=(--max-delete 100)
    fi
fi
GLOBAL_OPTIONS+=($DELETE_OPTIONS)


GLOBAL_OPTIONS+=($LOG_OPTIONS)

if test $# -gt 0;
then
# must contain the trailing /
    for subdir
    # in $@
    do
        # note: I do append the "/" after ROOT
        do_rsync --relative "$SOURCE_REPO/./$subdir"  $ROOT/
    done
else
    do_rsync $SOURCE_REPO/   $ROOT/
fi

if [ $? = 25 ]; then
    cecho red "the --max-delete limit stopped deletions" >&2
fi


# options:
# dry
# careful -- slow & limit deletes & verbose
# fast
