#!/bin/bash
## GPLv3
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

SLACK_VER="$(grep VERSION= /etc/os-release | cut -d\" -f2)"
Tff0000="\033[1;31m"
Tffffff="\033[00m"
T00ff00="\033[1;32m"
VERSION=0.0.22
SKIPCHECK=0
WGET=/usr/bin/wget
LOG=/dev/null

pathchoose() {
    for i in "$@"
    do
        if [ -r "$i" ]; then
            echo "$i"
            return
        fi
    done
}

if [ -e /etc/sport.conf ]; then
    source /etc/sport.conf
elif [ -e /etc/sport.conf-default ]; then
    source /etc/sport.conf-default
else
    REPOS=( ${SBOPATH:-/var/lib/sbopkg/SBo/$SLACK_VER} )
    MASTER=( ${MASTER:-"rsync://slackbuilds.org/slackbuilds/$SLACK_VER/"} )
fi

MAKEFLAGS="$MAKEOPTS $MAKEFLAGS"
export MAKEFLAGS

USBOPATH="$SBOPATH"
SBOPATH="$(pathchoose "$SBOPATH" /usr/{local,share,}/ports /var/lib/sbopkg/SBo/"$SLACK_VER")"

if ! [ "$SBOPATH" ]; then
    SBOPATH="${USBOPATH:-/var/lib/sbopkg/SBo/$SLACK_VER}"

    echo WARNING: Cannot find SBo repo. To fix this, either:
    echo - Set the SBOPATH env variable to point to the correct place
    echo - Create /etc/sport.conf and put the correct SBOPATH in it
    echo - Run sport rsync
    echo When you run sport rsync, the repo is placed in "$SBOPATH"
    echo
fi

MASTER="${MASTER:-"rsync://slackbuilds.org/slackbuilds/$SLACK_VER/"}"

export MAKEFLAGS="$MAKEOPTS"

CWD="$(pwd)"
CLEANMODE=0

ENDOFBUILDPRINT=""

exit_script() {
    printf %"s\n$ENDOFBUILDPRINT\n"
    exit 255
}

trap exit_script SIGINT SIGTERM

#set -e
## determine arch SUFFIX
case "$([ "$ARCH" ] && echo "$ARCH" || uname -m)" in
x86_64) SUFFIX="_x86_64" ;;
esac

## are we cleaning up after we are done?
## this catches a straggler "clean" command after all args
## i figure BSD'ers might do this by force of habit
CLN=$(echo "$@" | rev | cut -f1 -d" " )
if [ "$CLN" = "naelc" -o "$CLN" = "--naelc" ]; then
    CLEANMODE=2
fi

build_pkg() {
    # this function gets and md5checks the source(s)
    #   and then executes the .SlackBuild script

    local FILE="${1##*/}"

    # what arch do we need?
    # if we are x86_64 but x86_64 field is empty..
    if [ "$SUFFIX" ] && ! grep -q "DOWNLOAD$SUFFIX *= *\" *[^\"]" "$FILE.info"; then
    # pretend we are 32-bit
	local SUFFIX=""
    fi
    DEPS_NEEDED=""
    for dep in $REQUIRES
    do
        if [ -z "$(find /var/log/packages/ -iname "$(echo "$dep" | rev | cut -f1 -d"/" | rev)*")" ]; then
            DEPS_NEEDED+="$(echo "$dep" | rev | cut -f1 -d"/" | rev) "
        fi
    done
    if [ -n "$DEPS_NEEDED" ]; then
        printf "$Tff0000\nWARNING: These dependencies are missing. The build will probably fail:"
        printf "$DEPS_NEEDED ${Tffffff}"
    fi

    # create an array for downloadable files and md5sums
    ULINK=($(sed -n "/DOWNLOAD$SUFFIX/,/MD5SUM$SUFFIX/ s_tp.*//_&_1p" "$FILE.info" | tr -d '"\\'))
    MD5CHECK=($(sed -n "/MD5SUM$SUFFIX/,/DOWNLOAD$SUFFIX_/ s_[0-9][a-z]._&_1p" "$FILE.info" | cut -f2 -d= | tr -d '"\\'))

    #echo "DEBUG" ${ULINK[@]}

    # download the sauce
    # it would be faster backgrounding the wget instances,
    #  but that would make checking the return statii harder,
    #  and anyway the terminal output would be a mess
    for item in "${ULINK[@]}"; do
	$WGET $(echo "$item" | cut -f2 -d'=' | tr -d '"\\') || \
            { echo "Failed to download source file $item"; exit 1; }
    done

    # all sources have been wgotten
    # now check md5sums
    local count=0
    for item in ${MD5CHECK[@]}; do

	local TARBALL="$(basename "${ULINK[$count]}" | tr -d '"\ ')"
	local MD5SUM=$(md5sum "$TARBALL" | cut -f1 -d" ")
	let count++

        # previously, this always breaked
	if ! [ -r "$TARBALL" ]; then
	    break
	fi

	if ! [ "$item" = "$MD5SUM" ]
	then
	    echo "$TARBALL"
	    echo "Expected sum: $item"
	    echo "What we got:  $MD5SUM"
	    echo "O.o md5sums do not match. Continue? [Y/n]"
	    read CONTINUE
	    if [ "X$(echo "$CONTINUE" | tr [:upper:] [:lower:] | cut -b1)" != "Xy" ]; then
                exit 2
	    else
		echo "Boldly continuing with build..."
	    fi
	fi
    done

    if [ "$SWAPPER" ]; then
        if [ "$SB_MANDIR" ]; then
	    sed -i  "s%/usr/man%$SB_MANDIR%g"  "./$FILE.SlackBuild"
	    echo "user variable override: $SB_MANDIR"
        fi

        if [ "$SB_DOCDIR" ]; then
            sed -i  "s%/usr/doc%$SB_DOCDIR%g"  "./$FILE.SlackBuild"
	    echo "user variable override: $SB_DOCDIR"
        fi

        if [ "$SB_INFODIR" ]; then
            sed -i "s%/usr/info%$SB_INFODIR%g" "./$FILE.SlackBuild"
	    echo "user variable override: $SB_INFODIR"
        fi
    fi

    set -o pipefail
    if ! MAKEFLAGS="$MAKEOPTS" bash ./"$FILE.SlackBuild" |& tee -a $LOG
    then
        echo -e "${Tff0000}"
        if [ "$DEPS_NEEDED" ]; then
            echo -e "${Tff0000}The build failed, probably due to missing dependencies. These dependencies have not been installed:${Tffffff}"
            echo -e "$DEPS_NEEDED ${Tffffff}"
            ENDOFBUILDPRINT+="${Tff0000}Building ${FILE} failed. Missing dependencies: ${DEPS_NEEDED} ${Tffffff}"
        else
            echo -e "${Tff0000}The build failed!${Tffffff}"
            ENDOFBUILDPRINT+="${Tff0000}Building ${FILE} failed ${Tffffff} \n"
        fi
    else
        if [ "$DEPS_NEEDED" ]; then
            echo -e "${Tff0000}The following dependencies were not installed during the build, and so some features may be disabled:"
            echo -e "${Tff0000} $DEPS_NEEDED "
            ENDOFBUILDPRINT+="${Tff0000}${FILE} was built, but these dependencies were missing: ${DEPS_NEEDED} ${Tffffff} \n"
        else
            ENDOFBUILDPRINT+="${T00ff00}${FILE} was built ${Tffffff} \n"
        fi
    fi
    set +o pipefail
}

version_return() {
    echo "Slackport version ${VERSION} running on Slackware ${SLACK_VER}"
}

untar_pkg() {
    ## this function untars the SlackBuild archive as needed
    ## because SBo SlackBuilds are individually gzipped

    if [ ! -d "$SBOPATH/$CATEGORY/$PKG_NAME" ]; then
	tar -xf "$SBOPATH/$CATEGORY/$PKG_NAME.tar.gz" \
	    -C "$SBOPATH/$CATEGORY/" 2>/dev/null || \
	    if ! [ "$CLEANMODE" == 2 ]; then
	        echo "Errors were found; check the package name, and provide the category."
	        echo "$SBOPATH"
	        echo "$CATEGORY"
	        echo "$PKG_NAME"
                exit 3
	    fi
    fi
}

help() {
    echo "sport [commands] <arguments>"
    echo ""
    echo "sport search foo bar     = search your local Slackbuild tree for foo and bar"
    echo "sport list audio         = list all Slackbuilds in $SBOPATH/audio"
    echo "sport cat foo             = displays the README and .info file for foo"
    echo "sport install foo bar     = build and install foo and bar from your Slackbuild tree"
    echo "sport install .           = build and install a Slackbuild from the current directory"
    echo "sport i \$(<foo.list)     = build and install line-delimited list of packages in foo.list"
    echo "sport i --all foo         = install foo with all dependencies"
    echo "sport build foo           = build but do not install foo"
    echo "sport deps foo            = show unsatisfied dependencies required by foo"
    echo "sport deps --all foo      = show all dependencies for foo"
    echo "sport tree foo            = show all dependencies for foo, in tree view"
    echo "sport single foo          = show dependencies of foo, and only foo (no recursion)"
    echo "sport alternatives foo    = show packages from all port trees with a name exactly matching foo"
    echo "sport check foo bar       = fuzzy searches /var/log/packages for foo and bar"
    echo "sport clean foo           = remove source code from $SBOPATH/foo"
    echo "sport rsync               = synchronize $SBOPATH with upstream port tree"
    echo "See man page for more information"
    echo ""
}

cat_finder() {
    ## this function determines the path to whatever ARG
    ## the user has provided

    # did we get an absolute path?
    SLASH=$(echo $item | grep "/")

    if [ -z "$SLASH" ]; then
        # no we need to find the absolute path.
        #echo "Auto-detecting category..."
        declare -a ALTERNATIVES
        declare -a PKGREPOS
        for i in "${REPOS[@]}"
        do
            for j in $(find "${i}" -maxdepth 2 -name "${item}")
            do
                ALTERNATIVES+=( "$(echo $j | rev | cut -f2 -d"/" | rev)" )
                PKGREPOS+=( "${i}" )
            done
        done
        if [ ${#ALTERNATIVES[@]} == 0 ]; then
            echo "Package not found."
            exit
        fi
        if [ ${#ALTERNATIVES[@]} == 1 ]; then
            CATEGORY="${ALTERNATIVES[0]}"
            PKGREPO="${PKGREPOS[0]}"
        else
            echo "There are multiple providers for $(echo "${item}" | rev | cut -f1 -d"/" | rev)":
            echo ""
            for i in $(seq 1 ${#ALTERNATIVES[@]})
            do
                echo -n "$i: $(echo ${PKGREPOS[$((${i} - 1))]} | rev | cut -f1 -d"/" | rev)/${ALTERNATIVES[$((${i} - 1))]} "
            done
            echo ""
            echo -n "Provider to use:  "
            read PROVIDER
            CATEGORY="${ALTERNATIVES[$((${PROVIDER} - 1))]}"
            PKGREPO="${PKGREPOS[$((${PROVIDER} - 1))]}"
        fi
    else
	    #echo "Resolving category..."
        declare -a PKGREPOS
        CATEGORY=$(echo $item | cut -f1 -d"/")
        item=$(echo $item | cut -f2 -d"/")
        for i in "${REPOS[@]}"; do
            if [ -e "${i}/${CATEGORY}/${item}" ] || [ -e "${i}/${CATEGORY}/${item}.tar.gz" ]; then
                PKGREPOS+=( "${i}" )
            fi
        done
        if [ ${#PKGREPOS[@]} == 0 ]; then
	        echo "Package not found."
            exit
        fi
        if [ ${#PKGREPOS[@]} == 1 ]; then
            PKGREPO=${PKGREPOS[0]}
        else
            echo "There are multiple providers for ${item}:"
            echo ""
            for i in $(seq 1 ${#PKGREPOS[@]})
            do
            echo -n "$i: $(echo ${PKGREPOS[$((${i} - 1))]} | rev | cut -f1 -d"/" | rev)/${CATEGORY} "
            done
            echo ""
            echo -n "Provider to use:  "
            read PROVIDER
            PKGREPO="${PKGREPOS[$((${PROVIDER} - 1))]}"
        fi
    fi
}

declare -A printdeps

print_deps() {
    local pkg="$1"
    item="$pkg"
	cat_finder
    printdeps["$(basename "${pkg}" .tar.gz)"]=yes
	source "${PKGREPO}/${CATEGORY}/$(basename "${pkg}" .tar.gz)/"*.info
    for dep in $REQUIRES; do
        if [ -z ${printdeps["$dep"]} ] >/dev/null; then
            if [ "$SKIPCHECK" == 1 ] || [ -z "$(find /var/log/packages/ -iname "$(echo "$dep" | rev | cut -f1 -d"/" | rev)*")" ]; then
                print_deps $dep
            fi
        fi
    done
    printf %"s$pkg\n"
}

tree_deps() {
    cat_finder "$item"
	source "${PKGREPO}/${CATEGORY}/$(basename ${PKG_NAME} .tar.gz)/"*.info
    for dep in $REQUIRES; do
        echo "$(echo "$dep" | rev | cut -f1 -d"/" | rev)"
        item=$dep
        PKG_NAME=$dep
        tree_deps | sed -e 's/^/| /'
    done
}

local_finder() {
    ## this function determines if the user is actually
    ## wanting to work from the CWD

    if [ "$item" = . ]; then
	item="$(\ls *.SlackBuild | cut -f1 -d'.')"
        # What if the user tries to mix local and repo packages?
	REPOS=( "." )
	CATEGORY=.
	PKG_NAME="$(basename "$item" .tar.gz)"
    else
	# we are not wanting to work from CWD
	return 1
    fi
}

isopt() {
    [ "$1" = "$2" -o "$1" = "--$2" -o "$1" = "$3" -o "$1" = "-$3" ]
    return $?
}

### user interactions below
while [ True ]; do
if [ "$1" = "" ]; then
    break
elif [ "$1" = "--" ]; then
    shift 1
    break
elif isopt "$1" search s; then
    SEARCHMODE=1
elif isopt "$1" check k; then
    CHECKMODE=1
elif isopt "$1" list l; then
    LISTMODE=1
elif isopt "$1" cat c; then
    CATMODE=1
elif isopt "$1" queue q; then
    INSTALLMODE=1
    INSTALLER="${INSTALLER:-echo}"
elif isopt "$1" deps d; then
    DEPSMODE=1
elif isopt "$1" single s; then
    NRDEPMODE=1
elif isopt "$1" tree t; then
    TREEMODE=1
elif isopt "$1" install i; then
    INSTALLMODE=1
    INSTALLER="${INSTALLER:-/sbin/installpkg}"
elif isopt "$1" rsync r; then
    RSYNCMODE=1
    for iter in $(seq 0 ${#REPOS[@]}); do
	test -d ${REPOS[$iter]} || mkdir -p ${REPOS[$iter]}
	rsync -av ${MASTER[$iter]} ${REPOS[$iter]}
    done
    shift 1
elif isopt "$1" build-only || isopt "$1" build b; then
    BUILDMODE=1
    INSTALLER="${INSTALLER:-echo}"
elif isopt "$1" all a; then
    INSTALLDEPSMODE=1
    SKIPCHECK=1
    INSTALLER=${INSTALLER:-/sbin/installpkg}
elif isopt "$1" alternatives; then
    ALTMODE=1
elif isopt "$1" clean n; then
    CLEANMODE=1
elif isopt "$1" log; then
    LOG=/var/log/sport.log
elif isopt  "$1" help h; then
    true
elif isopt "$1" version V; then
    version_return
    exit 0
elif isopt "$1" verbose v; then
    version_return
    VERBOSE=1
elif isopt "$1" swap; then
    SWAPPER=1
else
    break
fi
shift 1
done

# everything else left at this point
# SHOULD be a package name, so..
# build an array of packages
ARG=(${@})
ARRAYSIZE=${#ARG[*]}

## Main Loop
n=0

if [ "$LISTMODE" != 1 ]; then
    if [ "$ARRAYSIZE" == 0 ]; then
        help
        exit
    fi
else
    if [ "$ARRAYSIZE" == 0 ]; then
        for i in "${REPOS[@]}"
        do
            echo $(echo $i | rev | cut -f1 -d"/" | rev):
            for dir in $(echo $i/*/)
            do
                echo $(echo $dir | rev | cut -f2 -d"/" | rev)/
            done
        done
        exit
    fi
fi

while [ True ]; do
    for item in "${ARG[@]}"; do

	CATEGORY="$(echo "$item" | cut -f1 -d"/")"
	PKG_NAME="$(basename "$item".tar.gz | cut -f2 -d"/")"

	unset ARG[${n}]
	let n++

	if [ "$SEARCHMODE" == 1 ]; then
	    cd $SBOPATH
            QUERY=$(echo "$item" | rev | cut -f1 -d"/" | rev)
	    # this separates searches when multiple searches are performed
	    # you can remove it if it bugs you
	    if [ "$VERBOSE" == 1 ]; then
		echo "---------------$QUERY---------------------------"
	    fi
        for i in "${REPOS[@]}"
        do
            for RESULT in $(find $i -maxdepth 2 -iname "*${QUERY}*.tar.*" | grep -v asc$ | rev | cut -f1,2,3 -d"/" | rev); do
            echo -e "$RESULT%"$(tar -xf $i/$RESULT -O 2>/dev/null | \
                                    grep handy-ruler -A1 | cut -f2 -d":" | tail -n1) ; done | column -s% -t
        done
        cd $CWD
	fi

	if [ "$CHECKMODE" == 1 ]; then
	    local_finder
	    CHECKER=$(echo "$item" | rev | cut -f1 -d"/" | rev)
            find /var/log/packages/ -iname "*${CHECKER}*"
	fi

	if [ "$LISTMODE" == 1 ]; then	
        for i in "${REPOS[@]}"
        do
            echo $(echo $i | rev | cut -f1 -d"/" | rev):
            if [ X"$item" != "X$i" ]; then
                QUERY=$(echo "$item" | rev | cut -f1 -d"/" | rev)
            for RESULT in $(ls $i/$QUERY | grep tar.gz | grep -v asc$ );
		    do echo -e "$RESULT%"$(tar -xf $i/$QUERY/$RESULT -O 2>/dev/null | \
		    grep handy-ruler -A1 | cut -f2 -d":" | tail -n1) ; done | column -s% -t
            else
                ls "${i}"
                break
            fi
        done
	fi

	if [ "$CATMODE" == 1 ]; then
	    # are we working from CWD or not?
	    local_finder "${item}"
	    # well, are we?
	    if [ "$CATEGORY" = "." ]; then
		cat *{.info,README}
	    else
		# if we are here
		# then we are NOT working from CWD
		# ergo, user thinks this is yum
		cat_finder "$item"
#		echo "category" "$CATEGORY"
		if [ "$VERBOSE" == 1 ]; then
		    echo "------------------------------------------"
		fi
		cat "${SBOPATH}/${CATEGORY}/$(basename ${PKG_NAME} .tar.gz)/"*{.info,README}
	    fi

	    if [ "$VERBOSE" == 1 ]; then
		echo "------------------------------------------"
		echo " " 
	    fi
	fi
	
    if [ "$DEPSMODE" == 1 ]; then
        print_deps $item
	ENDOFBUILDPRINT=" "
    fi

    if [ "$NRDEPMODE" == 1 ]; then
        # are we working from CWD or not?
	    local_finder "${item}"
	    # well, are we?
	    if [ "X$CATEGORY" == "X." ]; then
		    source "${item}.info"
            for dep in $REQUIRES; do
                #[ -e "/var/log/packages/$(echo "$dep" | rev | cut -f1 -d"/" | rev)*" ] || echo -n "$(echo "$dep" | rev | cut -f1 -d"/" | rev)"
                find /var/log/packages/ -iname "$(echo "$dep" | rev | cut -f1 -d"/" | rev)*" || echo $dep
            done
	    else
		    # if we are here
		    # then we are NOT working from CWD
		    # ergo, user thinks this is yum
		    cat_finder "$item"
		    source "${PKGREPO}/${CATEGORY}/$(basename ${PKG_NAME} .tar.gz)/"*.info
            for dep in $REQUIRES; do
                #[ -e "/var/log/packages/$(echo "$dep" | rev | cut -f1 -d"/" | rev)*" ] && echo -n "$(echo "$dep" | rev | cut -f1 -d"/" | rev) "
                if [ -z "$(find /var/log/packages/ -iname "$(echo "$dep" | rev | cut -f1 -d"/" | rev)*")" ]; then
                    echo -n "$(echo "$dep" | rev | cut -f1 -d"/" | rev) "
                fi
            done
            echo
	    fi
    fi
    if [ "$TREEMODE" == 1 ]; then
        echo $item
        tree_deps | sed -e 's/^/| /'
    fi
	if [ "$INSTALLMODE" == 1 ] && [ "$INSTALLDEPSMODE" != 1 ]; then
	    local_finder || cat_finder
	    START=$(find . -maxdepth 0 -iname "${item}.SlackBuild")
	    if [ "X$START" == "X" ]; then
		cd ${PKGREPO}/${CATEGORY}/$(basename $PKG_NAME .tar.gz) 2>/dev/null
	    else
		    echo "Working in current directory"
	    fi
	    build_pkg $(basename $PKG_NAME .tar.gz)
	    if ! [ "$BUILDMODE" = 1 ]; then
		$INSTALLER $(ls -Art1 /tmp/`basename $PKG_NAME .tar.gz`*t?z | tail -n1)
	    fi
	    cd $CWD
	fi

	if [ "$ALTMODE" == 1 ]; then
            for i in "${REPOS[@]}"
          do
        for j in $(find "${i}" -maxdepth 2 -name "${item}")
          do
        echo "$(echo ${j} | rev | cut -f-3 -d"/" | rev)"
        done
        done
	fi
	
	if [ "$INSTALLDEPSMODE" == 1 ] && [ "$INSTALLMODE" == 1 ]; then
        for pkg in $(print_deps $item); do
        item=$pkg
        PKG_NAME=$pkg
	    local_finder || cat_finder
	    START=$(find . -iname "${item}.SlackBuild" -maxdepth 0)
	    if [ "X$START" == "X" ]; then
            cd ${PKGREPO}/${CATEGORY}/$(basename $PKG_NAME .tar.gz) 2>/dev/null
	    else
		    echo "Working in current directory"
	    fi
	    build_pkg $(basename $PKG_NAME .tar.gz)
	    if [ "X$BUILDMODE" != "X1" ]; then
		    $INSTALLER $(ls -Art1 /tmp/`basename $PKG_NAME .tar.gz`*t?z | tail -n1)
	    fi
	    cd $CWD
        done
	fi	
        #XXX
	if [ "$CLEANMODE" -ge 1 ]; then
	    cat_finder "$item"
	    rm -r $SBOPATH/$CATEGORY/`basename $PKG_NAME .tar.gz`
	    rm /tmp/$PKG_NAME*SBo*t?z 2> /dev/null
	    tar -xf $SBOPATH/$CATEGORY/$PKG_NAME \
		-C $SBOPATH/$CATEGORY/
	fi

    done
    [ "$ENDOFBUILDPRINT" ] && echo -e "$ENDOFBUILDPRINT"
exit
done

