#!/bin/bash
#
#

# TODO: Allow regular DAPS

ME=$(basename "$0")


SCHEMA="/usr/share/xml/daps/schema/daps-autobuild.rnc"
#CONFIG="$(readlink -e "daps-autobuild.xml")"
#SCHEMA="$(readlink -e "daps-autobuild.rnc")"

#VERBOSE=1
CHECK_VCS=1
DEBUG=0
DO_RSYNC=1
RSYNC_DELETE=""
SECTIONS=""
SEND_REPORTS=0

RSYNC="/usr/bin/rsync"
CCECHO="/usr/bin/ccecho"
if [[ -x /usr/bin/xml ]]; then
    XMLSTARLET="/usr/bin/xml"
elif [[ -x /usr/bin/xmlstarlet ]]; then
    XMLSTARLET="/usr/bin/xmlstarlet"
else
    exit_on_error "Cannot find an xmlstarlet binary in /usr/bin/"
fi

FAILURES=0
#declare -A FAILED_BOOKS

_BUILDDIR=""
_DAPSROOT=""
_RSYNC_TARGET=""
_RSYNC_FLAGS=""

declare -a ALL_SETIDS

RUNDAPS=""

function usage {
    cat <<EOFhelptext
Usage:
  $ME --config <PATH TO CONFIG FILE> [OPTIONS]

Automatically build books defined in a config file and sync them to a server.
The config file is an XML file, see $ME --helpconfig for more information.

Specifying a config file with --config is mandatory, all other options are
optional.

Options:

  --config=<file>
    Path to config file. Can be specified relative or absolute.
    Default: unset

  --dcfiles="dc-file1 dc-file2"
    Space-separated list of dcfiles that should be build. Requires
    --sections to be set. The dc-files specified need to be defined
    in the given section
    Default: not set (all dc-files from the section file will be build)

  --debug
    In case of a build failure, show a verbose DAPS log.
    Default: disabled

  --force
    Manuals will only be rebuilt when the git/svn repository has changed
    since the last build. Use this option to force a rebuild even when the
    repository has not changed.
    Default: disabled (rebuild on changes only)

  --help
    This help message

  --helpconfig
    Show help on the config file.

  --list-sections
    Show all sections defined in the given config file

  --nosync
    Do not rsync the manuals.
    Default: disabled (rsync will be done)

  --notify
    In case of build failures send a notification to the e-mail address
    specified with <maintainer/> in the config file. Will be ignored if
    <maintainer/> is not set.
    Default: disabled (No notification e-mails)

  --schema=<file>
    Path to the schema used to validate the config file.
    Default: /usr/share/xml/daps/schema/daps-autobuild.rnc

  --sections="sect1 sect2"
    Space-separated list of sets that should be build. The sets need to be
    configured in the config file.
    Default: not set (all sets from the config file will be build)

  --validate
    Validate the config file specified with --config. Other options except
    --schema will be ignored.

EOFhelptext
}

function helpconfig {
    cat <<EOFhelptext
The config file for $ME needs to look like the following example. The general
section must only occur once, while the set section can occur multiple times.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE daps-autobuild>
<daps-autobuild>
 <!-- ;;;;;;;;;;;;;;;;;;;; The General Section ;;;;;;;;;;;;;;;;;;; -->
 <general>
  <builddir>Path to builddir. Mandatory.</builddir>
  <dapsroot>Path to DAPS directory. Mandatory.</dapsroot>
  <rsync>
   <target>Rsync target. Mandatory unless using --nosync.</target>
   <flags>Rsync flags (do not use --delete here). Optional.</flags>
  </rsync>
 </general>

 <!;;;;;;;;;;;;;;;;;;;; A Set Section ;;;;;;;;;;;;;;;;;;; -->
 <set id="Unique ID" meta="0|1 (optional, default 0)"
      remarks="0|1 (optional, default 0)"
      draft="0|1 (optional, default 0)">
  <dcfiles>Space-separated list of DC files. Mandatory.</dcfiles>
  <formats>
   Space-separated list of formats to build (html single-html pdf). Mandatory.
  </formats>
  <styleroot>Path to stylesheet directory. Optional.</styleroot>
  <fb_styleroot>Path to fallback stylesheet directory. Optional.</fb_styleroot>
  <maintainer>E-Mail address for notifications. Optional.</maintainer>
  <vcs type="git|svn (mandatory)">
   <checkout>Checkout directory. Mandatory.</checkout>
   <lastrev><!-- Leave empty, will be filled by script. Mandatory.--></latsrev>
  </vcs>
 </set>
EOFhelptext
}

function exit_on_error {
    # Print error message and exit with return code 1
    #
    # Expects 1 argument:
    #  * error message
    #
    echo -e "ERROR: ${1}" >&2
    exit 1;
}

function validate {
    jing -c "$SCHEMA" "$CONFIG" && return 0
    return 1
}

function check_element {
    # checks if array contains an element
    #
    # Expects two arguments:
    #  1. Element to check
    #  2. list (${array[@]})
    #
    local ELEMENT

    [[ -z $1 ]] && exit_on_error "Function check_element must be called with an item to check for"
    [[ -z $2 ]] && exit_on_error "Function check_element must be called with an array"

    for ELEMENT in "${@:2}"; do
        [[ "$ELEMENT" == "$1" ]] && return 0
    done
    return 1
}

function read_config_value {
    # reading element/attribute values from $CONFIG
    #
    # Expects 2 argument:
    #   1 xpath
    #   2 element name ("element") or attribute name ("@attribute")
    #
    local QUERY_OBJECT VALUE _XPATH

    [[ -z $1 ]] && exit_on_error "Function run_daps must be called with an xpath"
    [[ -z $2 ]] && exit_on_error "Function run_daps must be called with an element"

    _XPATH="$1"
    QUERY_OBJECT="$2"

    VALUE="$("$XMLSTARLET" sel -t -m "$_XPATH" -v "normalize-space($QUERY_OBJECT)" "$CONFIG")"
    echo "$VALUE"
    return 0
}

function read_general {
    # Read values from the general section of the config file
    #
    local _START_XPATH
    _START_XPATH="/daps-autobuild/general"

    _BUILDDIR="$(read_config_value "$_START_XPATH" "builddir")"
    if [[ -n $_BUILDDIR ]]; then
        [[ -d $_BUILDDIR ]] || exit_on_error "BUILDDIR directory $_BUILDDIR does not exist"
    fi

    _DAPSROOT="$(read_config_value "$_START_XPATH" "dapsroot")"
    if [[ -n $_DAPSROOT ]]; then
        [[ -d $_DAPSROOT ]] || exit_on_error "DAPSROOT directory $_DAPSROOT does not exist"
        CCECHO="$_DAPSROOT/bin/ccecho"
    fi

    if [[ 1 -eq $DO_RSYNC ]]; then
        _RSYNC_TARGET="$(read_config_value "${_START_XPATH}/rsync" "target")"
        _RSYNC_FLAGS="$(read_config_value "${_START_XPATH}/rsync" "flags")"
        [[ "$_RSYNC_FLAGS" =~ --delete ]] && $CCECHO "warn" "rsync flags contain --delete, manuals which fail to build will be deleted from server"
    fi
}


function process_git {
    # Update a Git repo and determine whether a rebuild is needed
    #
    # Return values:
    #
    # 0: Success
    # 1: git fetch failed
    # 2: Getting latest SHA hash failed
    # 3: Checking out branch failed
    # 4: More than remote (we don't know which to pick)
    # 5: No remote (we can't update the repo at all)
    #
    local GITBRANCH
    # get branch
    GITBRANCH="$(read_config_value "/daps-autobuild/set[@id='$SET']/vcs" "@branch")"
    [[ -z "$GITBRANCH" ]] && GITBRANCH='main'

    remote=$(git remote)
    [[ $(echo -e "$remote" | wc -l ) -ge 2 ]] && return 4
    [[ -z "$remote" ]] && return 5

    # no clue why we'd ever have local changes but it just happened, so
    # reset those
    git clean --quiet --force
    git reset --quiet --hard HEAD

    git fetch --quiet --prune "$remote" || return 1
    git checkout --quiet "remotes/$remote/$GITBRANCH" >/dev/null || return 3
    echo "  * Updated git branch $GITBRANCH"
    if [[ 1 -eq $CHECK_VCS ]]; then
        NEWREV="$(git rev-parse HEAD)" || return 2
        [[ "$NEWREV" != "$_LASTREV" ]] && REBUILD="1"
    fi
    return 0
}

function process_svn {
    # Update an SVN repo and determine whether a rebuild is needed
    #
    # Return values:
    #
    # 0: Success
    # 1: SVN up failed
    # 2: Getting latest revision number failed
    #
    svn up -q >/dev/null || return 1
    echo "  * Updated svn working copy"
    if [[ 1 -eq $CHECK_VCS ]]; then
        NEWREV="$(svnversion -c "$_CHECKOUT" . | sed -e 's/[MS]//g' -e 's/^[[:digit:]]*://')" || return 2
        [[ "$NEWREV" != "$_LASTREV" ]] && REBUILD="1"
    fi
    return 0
}


function run_daps {
    local BUILDDIR DAPS_BASE_CMD DCFILE _FORMAT NAME NAME_CMD_ARGS ORIG_IFS RESULT RUNDAPS _SET SUBCMD
    #
    # Create the DAPS command and run DAPS
    # Expects 4 Arguments:
    #  1. Format
    #  2. DC-file
    #  3. Set ID
    #
    # Return values:
    #  0: Success
    #  1: Failure (running DAPS failed)

    [[ -z $1 ]] && exit_on_error "Function run_daps must be called with a sub-command"
    [[ -z $2 ]] && exit_on_error "Function run_daps must be called with a DC-FILE"
    [[ -z $3 ]] && exit_on_error "Function run_daps must be called with a set ID"

    _FORMAT="$1"
    DCFILE="$2"
    _SET="$3"

    if [[ single-html = "$_FORMAT" ]]; then
        SUBCMD="html --single"
    else
        SUBCMD="$_FORMAT"
    fi

    NAME=${DCFILE//DC-/}
    BUILDDIR="${_BUILDDIR}/$_SET"

    # build the DAPS command
    #
    DAPS_BASE_CMD="${_DAPSROOT}/bin/daps --dapsroot $_DAPSROOT --builddir $BUILDDIR -d $DCFILE"
    [[ -n $_STYLEROOT ]]    && DAPS_BASE_CMD="$DAPS_BASE_CMD --styleroot=$_STYLEROOT"
    [[ -n $_FB_STYLEROOT ]] && DAPS_BASE_CMD="$DAPS_BASE_CMD --fb_styleroot=$_FB_STYLEROOT"
    RUNDAPS="$DAPS_BASE_CMD"
    [[ 1 -eq $DEBUG ]] && RUNDAPS="$RUNDAPS --debug"
    RUNDAPS="$RUNDAPS $SUBCMD --name $NAME"
    [[ -n $_DRAFT ]]        && RUNDAPS="$RUNDAPS --draft"
    [[ -n $_REMARKS ]]      && RUNDAPS="$RUNDAPS --remarks"
    if [[ -n $_META ]]; then
        [[ $SUBCMD = "html" || $SUBCMD = "pdf" ]] &&  RUNDAPS="$RUNDAPS --meta"
    fi

    echo -n "  * Building $_FORMAT version of $DCFILE ... "
    RESULT="$($RUNDAPS)"
    if [[ 0 -eq $? ]]; then
        $CCECHO "result" "Succeeded"
        SYNCDIR_DC="${SYNCDIR}/$NAME"
        NAME_CMD_ARGS="--name $NAME"
        mkdir -p "$SYNCDIR_DC"
        [[ -n $_DRAFT ]]        && NAME_CMD_ARGS="$NAME_CMD_ARGS --draft"
        [[ -n $_REMARKS ]]      && NAME_CMD_ARGS="$NAME_CMD_ARGS --remarks"
        if [[ -n $_META ]]; then
            [[ $SUBCMD = "html" || $SUBCMD = "pdf" ]] &&  NAME_CMD_ARGS="$NAME_CMD_ARGS --meta"
        fi
        NAME_CMD_ARGS="$NAME_CMD_ARGS 2>/dev/null"
        if [[ "pdf" = "$_FORMAT" ]]; then
            RESULT="$($DAPS_BASE_CMD pdf-name $NAME_CMD_ARGS)"
            mkdir -p "${SYNCDIR_DC}/$_FORMAT"
        elif [[ "html" = "$_FORMAT" ]]; then
            RESULT="$($DAPS_BASE_CMD html-dir-name $NAME_CMD_ARGS)"
        elif [[ "single-html" = "$_FORMAT" ]]; then
            RESULT="$($DAPS_BASE_CMD html-dir-name --single $NAME_CMD_ARGS)"
        fi
            mv "$RESULT" "${SYNCDIR_DC}/$_FORMAT"
        return 0
    else
        $CCECHO "warn" "Failed"
        FAILURES=$((FAILURES + 1))
        FAILED_BOOKS["$DC"]="${FAILED_BOOKS["$DC"]} $_FORMAT"
        ORIG_IFS="$IFS"
        IFS=$'\n'
        echo -e "\e[33m"
        for LINE in $RESULT; do
            echo -e "\t$LINE"
        done
        echo -e "\e[0m"
        IFS="$ORIG_IFS"
        return 1
    fi
}

function failure_notification {

    # Sends a failure report by mail
    #
    # Expects 2 arguments:
    # 1. SET name
    # 2. Reason for notification: daps|svn|git
    #
    # Return values:
    #  0: Success
    #  1: Failure because Postfix is not running
    #  2: Failure of mail command
    #
    local EMAIL KEY REASON _SET SUBJECT TEXT

    # exit with return code 1 of postfix is not running
    systemctl -q is-active postfix || return 1

    [[ -z $1 ]] && exit_on_error "Function failure_notification must be called with a set name"
    [[ -z $2 ]] && exit_on_error "Function failure_notification must be called with a reason"

    _SET="$1"
    REASON="$2"

    EMAIL="$(read_config_value "/daps-autobuild/set[@id='$SET']" "maintainer")"

    if [[ -n $EMAIL ]]; then

        SUBJECT="[DAPS Auto]: Error report on $_SET ($REASON)"

        case $REASON in
            daps)
                TEXT="While building manuals for set $_SET on ${FAILED_BOOKS["date"]}\n, errors occurred building the following DC-files\n\n"
                for KEY in "${!FAILED_BOOKS[@]}"; do
                    [[ "date" == "$KEY" ]] && continue
                    TEXT="$TEXT * $KEY:\t${FAILED_BOOKS["$KEY"]}\n"
                done
                ;;
            git)
                TEXT="Updating the Git checkout for $_SET failed on ${FAILED_BOOKS["date"]}\n"
                ;;
            svn)
                TEXT="Updating the SVN working copy for $_SET failed on ${FAILED_BOOKS["date"]}\n"
                ;;
        esac

        TEXT="$TEXT\n\nYours, DAPS Auto"

        echo -e "$TEXT" | mail -s "$SUBJECT" "$EMAIL" || return 2
    fi

    return 0
}


################ MAIN ################

#--------------------
# Read command line options

ARGS=$(getopt -o h -l config:,dcfiles:,debug,force,help,helpconfig,list-sections,nosync,notify,rsync,schema:,sections:,validate -n "$ME" -- "$@")

# Exit when getopt returns errors
#
[[ 0 -ne $? ]] && exit_on_error "Wrong command line argument"

eval set -- "$ARGS"

while true ; do
    case "$1" in
        --config)
            [[ -f $2 ]] || exit_on_error "Not a valid path for --config"
            CONFIG="$(readlink -e "$2")"
            shift 2
            ;;
        --dcfiles)
            DC_FILES_USER="$2"
            shift 2
            ;;
        --debug)
            DEBUG=1
            shift
            ;;
        --force)
            CHECK_VCS=0
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        --helpconfig)
            helpconfig
            exit 0
            ;;
        --list-sections)
            LIST_SECTIONS=1
            shift
            ;;
        --nosync)
            DO_RSYNC=0
            shift
            ;;
        --notify)
            SEND_REPORTS=1
            shift
            ;;
        --schema)
            SCHEMA="$2"
            shift 2
            ;;
        --sections)
            SECTIONS="$2"
            shift 2
            ;;
        --validate)
            validate
            if [[ 0 -ne $? ]]; then
                exit_on_error "$CONFIG does not validate"
            else
                echo "$CONFIG validates"
                exit 0
            fi
            ;;
        --) shift ; break ;;
        *) exit_on_error "Internal error while parsing the command line arguments!" ;;
    esac
done

# Check config/schema
[[ -z $CONFIG ]] && exit_on_error "You must specify a config file with --config"
if [[ -f $SCHEMA ]]; then
    SCHEMA="$(readlink -e "$SCHEMA")"
else
    exit_on_error "Not a valid path for --schema"
fi

# Chech whether --sections was specified with --dcfiles
[[ -n $DC_FILES_USER && -z $SECTIONS ]] && exit_on_error "You must specify one or more sections with --dcfiles"

#--------------------
# Validate the config file

jing -c "$SCHEMA" "$CONFIG"
[[ 0 -ne $? ]] && exit_on_error "$CONFIG does not validate"

#--------------------
# Get the set IDs

ALL_SETIDS=( $(xml sel -t -v "/daps-autobuild/set/@id" "$CONFIG") )
[[ 0 -eq ${#ALL_SETIDS[*]} ]] && exit_on_error "Could not find any SET IDs in the config file"

#----------
# If --list-sections was specified, print a list and exit

if [[ 1 -eq $LIST_SECTIONS ]]; then
    for ID in ${ALL_SETIDS[@]}; do
        echo "$ID"
    done
    exit 0
fi
#--


[[ -z $SECTIONS ]] && SECTIONS="${ALL_SETIDS[*]}"


#--------------------
# read the general data

read_general

for SET in $SECTIONS; do

    # check if SET is a valid section in the config file
    check_element "$SET" "${ALL_SETIDS[@]}"
    if [[ 1 -eq $? ]]; then
        $CCECHO "warn" "$SET not configured in config file, skipping!"
        continue
    fi

    _CHECKOUT=""
    _DCFILES=""
    _DRAFT=""
    _FB_STYLEROOT=""
    _FORMATS=""
    _LASTREV=""
    _META=""
    _REMARKS=""
    _STYLEROOT=""
    _VCS=""

    NEWREV=""
    START_XPATH="/daps-autobuild/set[@id='${SET}']"
    FAILURES="0"
    declare -A FAILED_BOOKS

    FAILED_BOOKS["date"]="$(date)"

    # set rebuild to "1" if we do not check for revisions
    if [[ 1 -eq $CHECK_VCS ]]; then
        REBUILD="0"
    else
        REBUILD="1"
    fi

    echo -e "$SET\n-------------------------"

    #--------------------
    # read the given set and store the data
    #

    # DC-files first
    #
    _DCFILES="$(read_config_value "$START_XPATH" "dcfiles")"

    # if DC-files were provided at the command-line, check whether
    # at least one of them appears in the current set
    # If so, set _DCFILES to the DC files common in both lists and
    # proceed. No only the DC-files provided by the user will be built
    #
    if [[ -n $DC_FILES_USER ]]; then
        # Check if user provided DC-file appears in _DCFILES
        _DCFILES=$(comm -1 -2 <(echo -e "$_DCFILES" | tr " " "\n" | sort -u) <(echo -e "$DC_FILES_USER" | tr " " "\n" | sort -u))
        if [[ -z $_DCFILES ]]; then
            $CCECHO "warn" "$SET does not contain the DC-file(s) specified!\nSkipping $SET.\n"
            continue
        fi
    fi

    _CHECKOUT="$(read_config_value "${START_XPATH}/vcs" "checkout")"
    _DRAFT="$(read_config_value "$START_XPATH" "@draft")"
    _FB_STYLEROOT="$(read_config_value "$START_XPATH" "fb-styleroot")"
    _FORMATS="$(read_config_value "$START_XPATH" "formats")"
    _LASTREV="$(read_config_value "${START_XPATH}/vcs" "lastrev")"
    _META="$(read_config_value "$START_XPATH" "@meta")"
    _REMARKS="$(read_config_value "$START_XPATH" "@remarks")"
    _STYLEROOT="$(read_config_value "$START_XPATH" "styleroot")"
    _VCS="$(read_config_value "${START_XPATH}/vcs" "@type")"

    # validity checks
    if [[ -n $_FB_STYLEROOT ]]; then
        if [[ ! -d $_STYLEROOT ]]; then
            $CCECHO "warn" "  FB_STYLEROOT directory '$_FB_STYLEROOT' does not exist,\n  using default"
           _FB_STYLEROOT=""
        fi
    fi

    if [[ -n $_STYLEROOT ]]; then
        if [[ ! -d $_STYLEROOT ]]; then
            $CCECHO "warn" "  STYLEROOT directory '$_STYLEROOT' does not exist,\n  using default"
           _STYLEROOT=""
        fi
    fi

    if [[ ! -d $_CHECKOUT ]]; then
        $CCECHO "warn" "  Checkout directory '$_CHECKOUT' does not exist!\n  Skipping $SET"
        continue
    fi

    pushd "$_CHECKOUT" >/dev/null

    #--------------------
    # update the checkout and check revisions
    if [[ $_VCS = "git" ]]; then
        process_git
        case $? in
            1)
                $CCECHO "warn" "  Running 'git fetch' in $_CHECKOUT failed!\n  Skipping $SET"
                failure_notification "$SET" "git"
                continue
                ;;
            2)
                $CCECHO "warn" "  Wasn't able to get the current SHA hash from Git"
                ;;
            3)
                $CCECHO "warn" "  Running 'git checkout <branch>' in $_CHECKOUT failed!\n  Skipping $SET"
                failure_notification "$SET" "git"
                continue
                ;;
            4)
                $CCECHO "warn" "  There is more than one remote configured for $_CHECKOUT and daps-autobuild cannot decide which one to use.\n  Skipping $SET"
                failure_notification "$SET" "git"
                continue
                ;;
            5)
                $CCECHO "warn" "  There is no remote configured in $_CHECKOUT and the repository cannot be updated.\n  Skipping $SET"
                failure_notification "$SET" "git"
                continue
                ;;
        esac
    elif [[ $_VCS = "svn" ]]; then
        process_svn
        case $? in
            1)
                $CCECHO "warn" "  Running 'svn up' in $_CHECKOUT failed!\n  Skipping $SET"
                failure_notification "$SET" "svn"
                continue
                ;;
            2)
                $CCECHO "warn" "  Wasn't able to get the current revision number from SVN"
                ;;
        esac
    fi


    #--------------------
    # build the manuals

    if [[ 1 -eq $REBUILD ]]; then

        # set up the sync directory; do not delete it if
        # $DC_FILES_USER ist set - in this case we only want to update
        # a single manual rather than replacing everything with a new build
        #
        SYNCDIR="${_BUILDDIR}/$SET/sync/$SET"
        if [[ -z $DC_FILES_USER ]]; then
            rm -rf "$SYNCDIR"
        fi
        mkdir -p "$SYNCDIR"

        for DC in $_DCFILES; do
            for _FORMAT in $_FORMATS; do
                 # Build!
                run_daps "$_FORMAT" "$DC" "$SET"
            done
        done
    else
        echo "  ==> Previous build still up-to-date, no rebuild needed"
    fi

    popd >/dev/null


    #--------------------
    # check for build failures

    if [[ 0 -ne $FAILURES ]]; then
        $CCECHO "warn" "  ==> Counted $FAILURES build failure(s)"
        # if there are build failures, do not run rsync with --delete
        RSYNC_DELETE=""
        # send an error report
        if [[ 1 -eq $SEND_REPORTS ]]; then
            failure_notification "$SET" "daps"
            case $? in
                1)
                    $CCECHO "warn" "Could not send error report (Postfix not running)"
                    ;;
                2)
                    $CCECHO "warn" "Could not send error report (mail command failed)"
                    ;;
            esac
        fi
    else
        # No build failures;
        #
        # if DC_FILES_USER was set, we do not want to change the revision in the
        # XML config file (because this affects the whole set).
        # We also do not want to run rsync with --delete
        #
        # if we rebuild a complete section, the revision number needs to get
        # updated and we also want rsny --delete
        #
        # In case a rebuild is forced, we do not check for a revision and
        # therefore NEWREV is is not set -> check if NEWREV is not empty
        # (seems better than to check for CHECK_VCS (from --force) - if
        # NEWREV is empty, we do not want to touch the lastrev entry in the
        # config file anyway
        #
        if [[ -z $DC_FILES_USER && -n $NEWREV ]]; then
            xml ed -P -L -u "/daps-autobuild/set[@id='$SET']/vcs/lastrev" -v "$NEWREV" "$CONFIG"
            RSYNC_DELETE="--delete"
        else
            RSYNC_DELETE=""
        fi
    fi

    #--------------------
    # rsync

    if [[ 1 -eq $DO_RSYNC && 1 -eq $REBUILD ]]; then
        $RSYNC $_RSYNC_FLAGS $RSYNC_DELETE $SYNCDIR $_RSYNC_TARGET
        if [[ 0 -eq $? ]]; then
            $CCECHO "result" "  Rsync successful"
        else
            $CCECHO "warn" "  Rsync failed"
        fi
    else
        $CCECHO "info" "  Skipping rsync"
    fi

    echo

# unset the array containing the failures
unset -v FAILED_BOOKS

done

exit 0
