#!/bin/sh
set -ex

# Move build artefacts to ~/src/DONE (== /src/DONE via the ~/src symlink),
# then delete anything in DONE older than ~50 days.
#
# Usage: mv_done [PATH...]
#   PATH    a file  -> processed directly (moved, regardless of extension)
#           a dir   -> scanned (top level only) for files whose extension is
#                      one of those listed in $EXTS below; sub-directories and
#                      files with no / unlisted extension are ignored
#   no args -> the current directory is scanned
#
# *.changes files are special: besides the .changes file itself, their
# sibling  <base>.build, <base>.buildinfo and <base>.*.upload files, plus
# every file named in their Files:/Checksums-*: sections, are moved along
# with them.  Everything else is moved on its own.

D="$HOME/src/DONE"
mkdir -p "$D"

# Extensions mv_done knows about (the former bare globs).
EXTS="deb upload done changes build buildinfo dsc gz xz bz2"

NL='
'

# --- bookkeeping -------------------------------------------------------------

cands=$NL        # newline-separated list of candidate files to process
moved=$NL        # newline-separated list of files already moved

in_cands() { case "$cands" in *"${NL}$1${NL}"*) return 0;; *) return 1;; esac; }
in_moved() { case "$moved" in *"${NL}$1${NL}"*) return 0;; *) return 1;; esac; }

add_cand() {
    if in_cands "$1"; then return; fi
    cands="$cands$1$NL"
}

mark_moved() { moved="$moved$1$NL"; }

do_move() {
    # Move $1 into $D, tolerating callers that race us (file already gone).
    [ -e "$1" ] || return 0
    mv -- "$1" "$D/"
    mark_moved "$1"
}

# --- extension test ----------------------------------------------------------

ext_ok() {
    # Return 0 iff the extension of filename $1 is one of $EXTS.
    n=$1
    case "$n" in
        *.*) ;;
        *)   return 1 ;;
    esac
    ext=${n##*.}
    for e in $EXTS; do
        if [ "$ext" = "$e" ]; then return 0; fi
    done
    return 1
}

# --- gather candidates -------------------------------------------------------

if [ $# -eq 0 ]; then
    set -- .
fi

for arg in "$@"; do
    [ -n "$arg" ] || continue
    # Drop trailing slashes (but keep a bare "/" as itself).
    arg=$(printf '%s' "$arg" | sed 's#/*$##')
    [ -n "$arg" ] || arg=/

    if [ -d "$arg" ]; then
        for ent in "$arg"/*; do
            if [ -e "$ent" ] && [ -f "$ent" ]; then
                if ext_ok "$(basename -- "$ent")"; then
                    add_cand "$ent"
                fi
            fi
        done
    elif [ -f "$arg" ]; then
        add_cand "$arg"
    else
        echo "mv_done: not a file or directory: $arg" >&2
    fi
done

# --- process .changes bundles ------------------------------------------------

process_one_change() {
    cf=$1
    dir=$(dirname -- "$cf")
    base=$(basename -- "$cf" .changes)

    # Read the referenced-file list while the .changes is still on disk.
    # Handles both plain and PGP-clearsigned files (armour lines are skipped).
    listed=$(awk '
        /^[[:space:]]*$/         { next }
        /^-----BEGIN PGP/        { next }
        /^-----END PGP/          { sec = 0; next }
        /^Hash:[[:space:]]/     { next }
        /^[[:space:]]/          { if (sec && !seen[$NF]++) print $NF; next }
        {
            h = $0; sub(/:.*/, "", h)
            if (h == "Files" || h == "Checksums-Sha1" ||
                h == "Checksums-Sha256" || h == "Checksums-Sha512")
                sec = 1
            else
                sec = 0
        }
    ' "$cf") || listed=

    do_move "$dir/$base.build"
    do_move "$dir/$base.buildinfo"

    set +f
    set -- "$dir/$base".*.upload
    [ -e "$1" ] || set --
    set -f
    for ul in "$@"; do do_move "$ul"; done

    # Referenced files are bare basenames; refuse anything with a slash.
    for lf in $listed; do
        [ -n "$lf" ] || continue
        case "$lf" in */*) continue;; esac
        do_move "$dir/$lf"
    done

    # Move the .changes file itself last.
    do_move "$cf"
}

OIFS=$IFS
IFS=$NL
set -f

for cf in $cands; do
    [ -n "$cf" ] || continue
    case "$cf" in
        *.changes) process_one_change "$cf" ;;
    esac
done

# --- move everything else ----------------------------------------------------

for f in $cands; do
    [ -n "$f" ] || continue
    case "$f" in *.changes) continue;; esac
    if in_moved "$f"; then continue; fi
    do_move "$f"
done

IFS=$OIFS
set +f

# --- housekeeping: drop ancient junk from DONE -------------------------------

cd "$D"
find . -mtime +50 -print0 | xargs -0r rm --
