#!/bin/sh

DESTDIR=$(pwd)/var-srcdeb
ERRORLOG=${DESTDIR}/errors.log
ERROR_TO_FILE=
VERBOSE=

VERSION="1.1"

print_version()
{
    echo "${VERSION}"
    exit 0
}

print_help()
{
    echo "\
Usage:
srcdeb [-o DESTDIR] [-l] [-d] [--] REPODIR
srcdeb --help | --version

Extract sources from debian repository in REPODIR to plain source directories.
Utility will apply according patches as well.
Default destination directory DESTDIR is ${DESTDIR}.
Packages will reside in ${DESTDIR}/package-one, ${DESTDIR}/package-two, etc.

-o <DIR>: change the output dir.
-l : dump errors during source extraction into log file DESTDIR/errors.log
-d : more verbose output: show messages from applied patches.
"
}

error()
{
    if test -n "${ERROR_TO_FILE}" ; then
        echo "$1" >> ${ERRORLOG}
    else
        echo "$1" >& 2
    fi
}

generic_error()
{
    echo "$1" >& 2
}

fatal_error()
{
    echo "$1" >& 2
    exit 1
}

apply_patch()
{
    pfile="$1"

    if test "${VERBOSE}" = '1' ; then
        patch -Np1 < ${pfile}
    else
        patch -Np1 < ${pfile} > /dev/null
    fi
}

main_srcdeb()
{

find "${ARCHSOURCE}" -type d   | while read packdir ; do
    echo "directory: $packdir"
    packname="${packdir##*/}"
    cd "${packdir}"

    debname="$( ls ${packname}_*.debian.tar.* 2> /dev/null )"
    debfile="${packdir}/${debname}"
    diffname="$( ls *[0-9]*.diff.[a-z]*z 2> /dev/null )"
    difffile="${packdir}/${diffname}"

    echo "package: $packname"

    origname="$( ls ${packname}_*.orig.tar.* 2> /dev/null )"
    # we need to remove files orig.tar.gz.something (like signatures: orig.tar.gz.asc)
    origname="${origname%%
*.orig.tar.*.[a-z]*}"
    origfile="${packdir}/${origname}"

    # final package name as registered in repo
    dscname="$( ls ${packname}_*.dsc 2> /dev/null )"
    name_half="${dscname%_*}"
    version_half="${dscname#*_}"
    version_half="${version_half%-*}"
    version_half="${version_half%.dsc}"
    dscname="${name_half}-${version_half}"

    if test ! -f "${origfile}" ; then

        if test -n "${origname}" ; then
            error "$packname"
            error "Incorrect detection of original archive: ${origname}"
            continue
        fi

        # package is debian-influenced and has no original archive
        other_arch="$( ls *.tar.*z 2> /dev/null )"

        if test -n "${other_arch}" ; then
            for arch in ${other_arch} ; do
                # the situation with sources without containing directory (see below) is not handled
                # renaming to dscname must not be needed as well
                tar xf ${arch} -C ${DESTDIR}/
            done
            if test ! -d ${DESTDIR}/${dscname} ; then
                error "${packname}"
                error "No correct source directory for pack without orig file"
            fi
        else
            error "$packname"
            error "origfile $origfile or other archives are not found"
        fi
        continue
    fi

    try_sourcename="$(tar tf ${origfile} | head -1)"
    sourcename="${try_sourcename#[^a-zA-Z0-9]*/}"
    sourcename="${sourcename%%/*}"
    if test "${sourcename}" = "${try_sourcename}" ; then
        # this means sources are packed without containing directory
        sourcename=
    fi
    if test -z "${dscname}" ; then
        error "${packname}"
        error "full name of the package is not determined: no .dsc file or incorrect detection"
        if test -z "${sourcename}" ; then
            error "name inside archive is not detected as well. Giving up."
            continue
        else
            error "Trying to use name inside archive as full package name"
            dscname="${sourcename}"
        fi
    fi
    if test -z "${sourcename}" ; then
        mkdir -p ${DESTDIR}/${dscname}
        tar xf "${origfile}" -C ${DESTDIR}/${dscname}
    else
        tar xf "${origfile}" -C ${DESTDIR}
        if test "${sourcename}" != "${dscname}" ; then
            if test -d ${DESTDIR}/${dscname} ; then
                echo "Removing previous ${DESTDIR}/${dscname}"
                rm -rf ${DESTDIR}/${dscname}
            fi
            mv "${DESTDIR}/${sourcename}" ${DESTDIR}/${dscname}
        fi
    fi
    pack_destdir=${DESTDIR}/${dscname}

    echo "original archive: $origfile"

    if test ! -d ${pack_destdir} ; then
        error "$packname"
        error "dir with sources ${pack_destdir} had not been created correctly"
        continue
    fi

    additional_origs="$( ls ${packname}_*.orig-*.tar.* 2> /dev/null )"
    for add_orig in ${additional_origs} ; do
        tar xf ${add_orig} -C ${pack_destdir}/
    done

    if test -f "${debfile}" ; then
        echo "debian metainfo archive: $debfile"
        tar xf ${debfile} -C ${pack_destdir}/
    elif test -n "${debname}" ; then
        error "$packname"
        error "Incorrect detection of debian metainfo archive: ${debname}"
    fi

    cd ${pack_destdir}/
    if test -f debian/patches/series ; then
        for singlepatch in $(sed -e '/^$/d ; /^#/d ; s/ .*//' debian/patches/series) ; do
            if test -f debian/patches/${singlepatch} ; then
                apply_patch debian/patches/${singlepatch}
            else
                error "$packname"
                error "patch ${singlepatch} is listed in file patches/series, but is absent in patches/"
                continue
            fi
        done
    fi

    if test -f "${difffile}" ; then
        mkdir -p debian/
        zcat ${difffile} > debian/package.diff
        apply_patch debian/package.diff
    elif test -n "${diffname}" ; then
        error "$packname"
        error "Incorrect detection of diff file: ${diffname}"
    fi
    
done

}

check_args()
{
    if test -z "${ARCHSOURCE}" ; then
        generic_error "Specify the directory with desired sources:"
        print_help
        exit 2
    fi

    if test ! -d "${ARCHSOURCE}" ; then
        generic_error "Incorrect source directory ${ARCHSOURCE} is given."
        exit 2
    fi

    ARCHSOURCE=$(cd ${ARCHSOURCE} ; pwd -P)

    mkdir -p "${DESTDIR}"
    DESTDIR=$(cd ${DESTDIR} ; pwd -P)
    touch "${DESTDIR}/try" || \
    (
        generic_error "Writing to output directory ${DESTDIR} is not available."
        exit 3
    )
    rm "${DESTDIR}/try"

    if test -n "$ERROR_TO_FILE" ; then
        : > ${ERRORLOG}
    fi
}


in_arg_cycle=1

while [ -n "$in_arg_cycle" ] ; do
    arg="$1"
    in_arg_cycle=

    checked_arg="${arg#-}" || true
    if [ "$checked_arg" != "${arg}" ] ; then
        in_arg_cycle=1
        shift

        case ${checked_arg} in
            o)
                DESTDIR="$1"
                ERRORLOG=${DESTDIR}/errors.log
                shift || fatal_error "No argument for option -o" 
            ;;
            l)
                ERROR_TO_FILE="1"
            ;;
            d)
                VERBOSE="1"
            ;;
            -version)
                print_version
            ;;
            -help)
                print_help
                exit 0
            ;;
            -)
                in_arg_cycle=
            ;;
            *)
                fatal_error "Unknown option ${arg}"
            ;;
        esac
    fi
done

ARCHSOURCE="${1}"

check_args
main_srcdeb

# ---------------------------------------------------------

# Copyright (c) 2020 Random Crew.
#
# Available under ISC License.

# ---------------------------------------------------------

