#!/usr/bin/env bash
#
#  cards
# 
#  Copyright (c) 2000 - 2005 Per Liden
#  Copyright (c) 2006 - 2013 by CRUX team (http://crux.nu)
#  Copyright (c) 2013 - 2020 by NuTyX team (http://nutyx.org)
#  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 2 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, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
#  USA.
#

##
# error codes
E_GENERAL=1
E_PKGFILE=2    # invalid Pkgfile
E_DIR_PERM=3   # (source/build) directory missing or missing read/write permission
E_DOWNLOAD=4   # error during download
E_UNPACK=5     # error during unpacking of source file(s)
E_MD5=6        # md5sum verification failed
E_FOOTPRINT=7  # footprint check failure
E_BUILD=8      # error while running 'build() function'
E_INSTALL=9    # error while installing the package via 'pkgadd'
E_DEPS=10      # error while searching runtime deps via 'pkginfo -b'
E_ALIAS=11     # error duplicate alias

debug() {
        if [[ "$PKGMK_DEBUG" == "yes" ]]; then
                echo "=== DEBUG: $1"
        fi
}
info() {
        echo "=======> $1"
}
warning() {
        info "WARNING: $1" >&2
}
error() {
        info "ERROR: $1" >&2
}
abort_on_build() {
        error $1
        exit $E_BUILD
}
have_function() {
        declare -f "$1" >/dev/null
}
get_dependencies() {
        [ -z $PKGMK_DEPENDS ] && return
        info "Get dependencies ..."
        local NAME COLLECTION PKGNAME
        i=0
        for COLLECTION in ${PKGMK_COLLECTIONS[@]}; do
                for NAME in ${PKGMK_DEPENDS[@]}; do
                        [ ! -d $(dirname $DIR)/$COLLECTION/${NAME%.*} ] && continue
                        for FILE in $(find $(dirname $DIR)/$COLLECTION/${NAME%.*}/ \
                                -name "${NAME}*.$PKGMK_PACKAGE_EXT*"); do
                                PKGNAME=$(get_package_name $(basename $FILE))
                                [ "$(pkginfo -b $PKGNAME)"  != 0 ] && continue
                                DEPENDENCIES[i]="$PKGNAME:$FILE"
                                i=$((i+1))
                        done
                done
        done
}
remove_dependencies() {
        if [ ! -z $DEPENDENCIES ];then
                for i in ${DEPENDENCIES[@]}; do
                        pkgrm $(echo $i|cut -d ":" -f1)
                done
        fi
}
get_collections() {
        local FILE DIR
        for FILE in $(find .. -name $PKGMK_PKGFILE | sort); do
                DIR="$(dirname $FILE)/"
                if [ -d $DIR ]; then
                        if [ -z "$COLLECTIONS" ]; then
                                COLLECTIONS="$(basename $DIR)"
                        else
                                COLLECTIONS="$COLLECTIONS $(basename $DIR)"
                        fi
                fi
        done
}
get_filename() {
        if [[ $1 =~ ^(http|https|ftp|file)://.*/(.+) ]]; then
                echo "$PKGMK_SOURCE_DIR/${BASH_REMATCH[2]}"
        else
                echo $1
        fi
}
check_pkgfile() {
        local SIZE NAME_TEST RELEASE_TEST DESC_TEST
        if [ "${name}" == "" ]; then
                error "Variable 'name' not initiated or not found in $PKGMK_PKGFILE."
                exit $E_PKGFILE
        fi
        if [ "$(type -t build)" != "function" ] && [ "$PKGMK_PKGFILE" == "Pkgfile" ]; then
                error "Function 'build' not specified in $PKGMK_PKGFILE."
                exit $E_PKGFILE
        fi
        NAME_TEST="$(echo "${name}" | sed 's@[[a-z0-9]*[-_+]*]*@@g')"
        if [ "$NAME_TEST" != "" ]; then
                error "Variable 'name' contains following illegal characters: $NAME_TEST"
                exit $E_PKGFILE
        fi
        if [ ${#name} -gt 50 ];then
                error "Variable 'name' length higher then 50 characters."
                exit $E_PKGFILE
        fi
        if [ "${version}" == "" ]; then
                error "Variable 'version' not initiated or not found in $PKGMK_PKGFILE."
                exit $E_PKGFILE
        fi
        if [ "${release}" == "" ]; then
                error "Variable 'release' not initiated or not found in $PKGMK_PKGFILE."
                exit $E_PKGFILE
        fi
        RELEASE_TEST="$(echo "${release}" | sed 's@[0-9]*@@g')"
        if [ "$RELEASE_TEST" != "" ]; then
                error "Variable 'release' contains following illegal characters: $RELEASE_TEST"
                exit $E_PKGFILE
        fi
        if [ $release -gt  99999999 ]; then
                error "Variable 'release' outside limit which is '99999999'"
                exit $E_PKGFILE
        fi
        DESC_TEST="$(echo "${description}" | grep '#')"
        if [ "$DESC_TEST" != "" ]; then
                error "Variable 'description' contains the '#' illegal character"
                exit $E_PKGFILE
        fi

        if [ "${description}" == "" ]; then
                warning "Variable 'description' not initiated or not found in $PKGMK_PKGFILE."
        fi
        SIZE=${#description}
        if [ $SIZE -gt 110 ];then
                error "Variable 'description' length higher then 110 characters, please resize to 110 char ]"
                info "$description <== $SIZE characters"
                exit $E_PKGFILE
        fi
        if [ ! -z "$alias" ];then
                get_collections
                # Check if any alias is already a port name
                for a in ${alias}
                do
                        for p in $COLLECTIONS
                        do
                                [ "$p" == "${name}" ] && continue
                                if [ "$a" == "$p" ];then
                                        error "$a is a port, duplicate alias"
                                        exit $E_ALIAS
                                fi
                                ( unset alias
                                . ../$p/$PKGMK_PKGFILE
                                if [ ! -z $alias ];then
                                        for ca in ${alias}
                                        do
                                                if [ "$ca" == "$a" ];then
                                                        error "$a is a alias of $p, duplicate alias"
                                                        exit $E_ALIAS
                                                fi

                                        done
                                        unset alias
                                fi ) || exit $E_ALIAS
                        done
                done
        fi
}
get_package_name() {
        local NAME EXT
        if [  "${1:$((${#1} - ${#PKGMK_PACKAGE_EXT} ))}" == "$PKGMK_PACKAGE_EXT" ]; then
                EXT=$PKGMK_PACKAGE_EXT
        else
                EXT=$PKGMK_PACKAGE_EXT.$PKGMK_COMPRESSION_MODE
        fi
        NAME="$(echo $1|sed "s/$PKGMK_ARCH.$EXT//"|sed "s/any.$EXT//")"

        echo "${NAME:0:$((${#NAME} - 10 ))}"
}
get_package_builddate() {
        local NAME BUILD_DATE EXT
        if [  "${1:$((${#1} - ${#PKGMK_PACKAGE_EXT} ))}" == "$PKGMK_PACKAGE_EXT" ]; then
                EXT=$PKGMK_PACKAGE_EXT
        else
                EXT=$PKGMK_PACKAGE_EXT.$PKGMK_COMPRESSION_MODE
        fi
        NAME="$(get_package_name $1)"
        BUILD_DATE="$(echo $1|sed "s/$PKGMK_ARCH.$EXT//"|sed "s/any.$EXT//")"
        echo "${BUILD_DATE:$((${#NAME}))}"
}
get_package_extension() {
        local EXTENSION
        if [  "${1:$((${#1} - ${#PKGMK_PACKAGE_EXT} ))}" == "$PKGMK_PACKAGE_EXT" ]; then
                EXTENSION=".$PKGMK_PACKAGE_EXT"
        else
                EXTENSION=".$PKGMK_PACKAGE_EXT.$PKGMK_COMPRESSION_MODE"
        fi
        echo $EXTENSION
}
get_package_arch() {
        local NAME BUILD_DATE EXTENSION
        NAME="$(get_package_name $1)"
        BUILD_DATE="$(get_package_builddate $1)"
        EXTENSION="$(get_package_extension $1)"
        echo $1|sed "s/$NAME$BUILD_DATE//"|sed "s/$EXTENSION//"
}
get_categories() {
        local FILE CANDIDATS CATEGORIES OLDIFS
        for FILE in $(find $PKG/usr/share/applications -type f 2>/dev/null);do
                CANDIDATS+=$(grep ^Categories $FILE|cut -d "=" -f2)
        done
        if [ ! -z $CANDIDATS ];then
                OLDIFS=$IFS
                IFS=";"
                CATEGORIES=$(for i in ${CANDIDATS[@]};do echo $i;done|sort|uniq)
                IFS=$OLDIFS
                categories=$(for i in ${CATEGORIES[@]};do echo -n "$i ";done)
        fi
}
check_directory() {
        if [ ! -d $1 ]; then
                error "Directory '$1' does not exist."
                exit $E_DIR_PERM
        elif [ ! -w $1 ]; then
                error "Directory '$1' not writable."
                exit $E_DIR_PERM
        elif [ ! -x $1 ] || [ ! -r $1 ]; then
                error "Directory '$1' not readable."
                exit $E_DIR_PERM
        fi
}
check_file() {
        if [ -e $1 ] && [ ! -w $1 ]; then
                error "File '$1' is not writable."
                exit $E_GENERAL
        fi
}
test_download_file_with_curl() {
        if [ ! "$(type -p curl)" ]; then
                error "Command 'curl' not found."
                exit $E_GENERAL
        fi
        DOWNLOAD_OPTS="--ftp-pasv --retry 3 --retry-delay 3 \
        -R --head --silent --output /dev/null --fail"

        error=$E_DOWNLOAD

        curl $DOWNLOAD_OPTS $2
        error=$?
        if [ $error != 0 ]; then
                error "Downloading '$2' failed."
                return $E_DOWNLOAD
        fi
        return 0
}
download_file_with_curl() {
        if [ ! "$(type -p curl)" ]; then
                error "Command 'curl' not found."
                exit $E_GENERAL
        fi
        DOWNLOAD_OPTS="--ftp-pasv --retry 3 --retry-delay 3 \
        -R -o $1 --fail -#"

        error=1

        curl $DOWNLOAD_OPTS $2
        error=$?
        if [ $error != 0 ]; then
                error "Downloading '$2' failed."
                exit $E_DOWNLOAD
        fi
}
test_download_file() {
        tdf_error=$E_DOWNLOAD
        all_good=$E_DOWNLOAD
        LOCAL_FILENAME="$(get_filename $FILE)"

        if [ ! "$(type -p wget)" ]; then
                test_download_file_with_curl $LOCAL_FILENAME $1
                tdf_error=$?
                if [ $tdf_error == 0 ]; then
                        all_good=0
                        break
                fi
        else
                DOWNLOAD_OPTS="-q --spider --no-check-certificate"
        
                BASENAME="${1/*\//}"
                for REPO in ${PKGMK_SOURCE_MIRRORS[@]}; do
                        REPO="$(echo $REPO | sed 's|/$||')"
                        wget $PKGMK_WGET_OPTS $DOWNLOAD_OPTS $REPO/$BASENAME
                        tdf_error=$?
                        debug "test_download_file: 1 tdf_error: $tdf_error while retrieving: $REPO/$BASENAME"
                        if [ $tdf_error == 0 ]; then
                                all_good=0
                                break
                        fi
                done

                debug "test_download_file: 2 tdf_error: $tdf_error"
                if [ $tdf_error != 0 ]; then
                        wget $PKGMK_WGET_OPTS $DOWNLOAD_OPTS $FILE
                        tdf_error=$?
                        debug "test_download_file: 3 tdf_error: $tdf_error"
                        if [ $tdf_error == 0 ]; then
                                all_good=0
                        fi
                fi
                
                debug "test_download_file: 4 all_good: $all_good"
        
                return $all_good
        
        fi
}
download_file() {
        info "Downloading '$1'."

        LOCAL_FILENAME="$(get_filename $FILE)"

        if [ ! "$(type -p wget)" ]; then
                warning "Command 'wget' not found."
                info "You should install 'wget' as soon as possible, trying with curl ..."
                download_file_with_curl $LOCAL_FILENAME $1
        else
                LOCAL_FILENAME_PARTIAL="$LOCAL_FILENAME.partial"
                DOWNLOAD_OPTS="--passive-ftp --no-directories --tries=3 --waitretry=3 \
                --directory-prefix=$PKGMK_SOURCE_DIR \
                --output-document=$LOCAL_FILENAME_PARTIAL --no-check-certificate"

                if [ -f "$LOCAL_FILENAME_PARTIAL" ]; then
                        info "Partial download found, trying to resume"
                        RESUME_CMD="-c"
                fi

                error=1

                BASENAME="${1/*\//}"
                for REPO in ${PKGMK_SOURCE_MIRRORS[@]}; do
                        REPO="$(echo $REPO | sed 's|/$||')"
                        wget $RESUME_CMD $DOWNLOAD_OPTS $PKGMK_WGET_OPTS $REPO/$BASENAME
                        error=$?
                        if [ $error == 0 ]; then
                                break
                        fi
                done

                if [ $error != 0 ]; then
                        while true; do
                                wget $RESUME_CMD $DOWNLOAD_OPTS $PKGMK_WGET_OPTS $1
                                error=$?
                                if [ $error != 0 ] && [ "$RESUME_CMD" ]; then
                                        info "Partial download failed, restarting"
                                        rm -f "$LOCAL_FILENAME_PARTIAL"
                                        RESUME_CMD=""
                                else
                                        break
                                fi
                        done
                fi
        
                if [ $error != 0 ]; then
                        error "Downloading '$1' failed."
                        exit $E_DOWNLOAD
                fi
        
                mv -f "$LOCAL_FILENAME_PARTIAL" "$LOCAL_FILENAME"
        fi
}
test_download_sources() {
        local FILE LOCAL_FILENAME
        nb_err=0
        for FILE in ${source[@]}; do
                LOCAL_FILENAME="$(get_filename $FILE)"
                debug "test_download_sources: $FILE => $LOCAL_FILENAME"
                if [ ! -e $LOCAL_FILENAME ]; then
                        if [ "$LOCAL_FILENAME" = "$FILE" ]; then
                                error "Source file '$LOCAL_FILENAME' not found (can not be downloaded, URL not specified)."
                                nb_err+=1
                        else
                                test_download_file $FILE
                                tds_error=$?
                                debug "test_download_sources: $FILE => tds_error=$tds_error"
                                if [ "$tds_error" == "$E_DOWNLOAD" ]; then
                                        error "Source file '$FILE' not found (can not be downloaded, URL not specified)."
                                        nb_err+=1
                                fi
                        fi
                fi
        done
        if [ $nb_err == 0 ]; then
                info "All sources are correct"
                exit 0
        fi
        exit $E_DOWNLOAD
}
download_source() {
        local FILE LOCAL_FILENAME

        for FILE in ${source[@]}; do
                LOCAL_FILENAME="$(get_filename $FILE)"
                if [ ! -e $LOCAL_FILENAME ]; then
                        if [ "$LOCAL_FILENAME" = "$FILE" ]; then
                                error "Source file '$LOCAL_FILENAME' not found (can not be downloaded, URL not specified)."
                                exit $E_DOWNLOAD
                        else
                                if [ "$PKGMK_DOWNLOAD" = "yes" ]; then
                                        download_file $FILE
                                else
                                        error "Source file '$LOCAL_FILENAME' not found (use option -d to download)."
                                        exit $E_DOWNLOAD
                                fi
                        fi
                fi
        done
}
remove_source() {
        local FILE LOCAL_FILENAME
        for FILE in ${source[@]}; do
                LOCAL_FILENAME="$(get_filename $FILE)"
                if [ -e $LOCAL_FILENAME ] && [ "$LOCAL_FILENAME" != "$FILE" ]; then
                        info "Removing $LOCAL_FILENAME"
                        rm -f $LOCAL_FILENAME
                fi
        done
}
unpack_source() {
        local FILE LOCAL_FILENAME COMMAND
        
        for FILE in ${source[@]}; do
                LOCAL_FILENAME="$(get_filename $FILE)"
                case $LOCAL_FILENAME in
                        *.tar|*.tar.gz|*.tar.Z|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.lzma|*.zip|*.tar.zst|*.rpm|*.tar.lz|*.7z|*.deb)
                                if [ "$PKGMK_IGNORE_UNPACK" != "yes" ];then
                                        COMMAND="bsdtar -p -o -C $SRC -xf $LOCAL_FILENAME"
                                else
                                        COMMAND="cp $LOCAL_FILENAME $SRC"
                                fi ;;
                        *)
                                COMMAND="cp $LOCAL_FILENAME $SRC" ;;
                esac

                echo "$COMMAND"

                $COMMAND

                if [ $? != 0 ]; then
                        if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                                rm -rf $PKGMK_WORK_DIR
                        fi
                        error "Building '$TARGET' failed."
                        exit $E_UNPACK
                fi
        done
}
get_packages_list() {
        local i ARCH
        i=0
        unset TARGETS
        for ARCH in $PKGMK_ARCH any; do
                for FILE in $(find $PKGMK_PACKAGE_DIR -name "${name}*$ARCH.$PKGMK_PACKAGE_EXT*"); do
                        TARGETS[i]="$(basename $FILE)"
                        i=$((i+1))
                done
        done
}
make_md5sum() {
        local FILE LOCAL_FILENAMES
        
        if [ "$source" ]; then
                for FILE in ${source[@]}; do
                        LOCAL_FILENAMES="$LOCAL_FILENAMES $(get_filename $FILE)"
                done
                
                md5sum $LOCAL_FILENAMES | sed -e 's|  .*/|  |' | sort -k 2
        fi
}
make_footprint() {
if ! (echo $TARGET| grep "kernel" >/dev/null); then
        pkginfo --footprint $TARGET | \
                sed "s|\tlib/modules/$(uname -r)/|\tlib/modules/<kernel-version>/|g" | \
                sort -k 3
else
        pkginfo --footprint $TARGET | sort -k 3
fi
}
check_md5sum() {
        local FILE="$PKGMK_WORK_DIR/.tmp"

        cd $PKGMK_ROOT
        
        if [ -f $PKGMK_MD5SUM ]; then
                make_md5sum > $FILE.md5sum
                sort -k 2 $PKGMK_MD5SUM > $FILE.md5sum.orig
                diff -w -t -U 0 $FILE.md5sum.orig $FILE.md5sum | \
                        sed '/^@@/d' | \
                        sed '/^+++/d' | \
                        sed '/^---/d' | \
                        sed 's/^+/NEW       /g' | \
                        sed 's/^-/MISSING   /g' > $FILE.md5sum.diff
                if [ -s $FILE.md5sum.diff ]; then
                        error "Md5sum mismatch found:"
                        cat $FILE.md5sum.diff >&2

                        if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                                rm -rf $PKGMK_WORK_DIR
                        fi

                        if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
                                error "Md5sum not ok."
                                exit $E_MD5
                        fi

                        error "Building '$TARGET' failed."
                        exit $E_MD5
                fi
        else
                if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
                        if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                                rm -rf $PKGMK_WORK_DIR
                        fi
                        info "Md5sum not found."
                        exit $E_MD5
                fi
                
                warning "Md5sum not found, creating new."
                make_md5sum > $PKGMK_MD5SUM
        fi

        if [ "$PKGMK_CHECK_MD5SUM" = "yes" ]; then
                if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                        rm -rf $PKGMK_WORK_DIR
                fi
                info "Md5sum ok."
                exit 0
        fi
}
strip_files() {
        local FILE FILTER
        
        cd $PKG
        
        if [ -f $PKGMK_ROOT/$PKGMK_NOSTRIP ]; then
                FILTER="grep -v -f $PKGMK_ROOT/$PKGMK_NOSTRIP"
        else
                FILTER="cat"
        fi

        find . -type f -printf "%P\n" | $FILTER | while read FILE; do
                case $(file -b "$FILE") in
                *ELF*executable*not\ stripped)
                        strip --strip-all "$FILE"
                        ;;
                *ELF*shared\ object*not\ stripped)
                        strip --strip-unneeded "$FILE"
                        ;;
                current\ ar\ archive)
                        strip --strip-debug "$FILE"
                esac
        done
}
compress_manpages() {
        local FILE DIR TARGET

        cd $PKG
        
        find . -type f -path "*/share/man*/*" | while read FILE; do
                if [ "$FILE" = "${FILE%%.gz}" ]; then
                        gzip -9 "$FILE"
                fi
        done
        
        find . -type l -path "*/share/man*/*" | while read FILE; do
        TARGET="$(readlink -n "$FILE")"
                TARGET="${TARGET##*/}"
                TARGET="${TARGET%%.gz}.gz"
                rm -f "$FILE"
                FILE="${FILE%%.gz}.gz"
                DIR="$(dirname "$FILE")"

                if [ -e "$DIR/$TARGET" ]; then
                        ln -sf "$TARGET" "$FILE"
                fi
        done
        find . -type f -path "*/share/info/*" | while read FILE; do
                if [ "$FILE" = "${FILE%%.gz}" ]; then
                        gzip -9 "$FILE"
                fi
        done
        find . -type l -path "*/share/info/*" | while read FILE; do
                TARGET="$(readlink -n "$FILE")"
                TARGET="${TARGET##*/}"
                TARGET="${TARGET%%.gz}.gz"
                rm -f "$FILE"
                FILE="${FILE%%.gz}.gz"
                DIR="$(dirname "$FILE")"

                if [ -e "$DIR/$TARGET" ]; then
                        ln -sf "$TARGET" "$FILE"
                fi
        done
}
check_footprint() {
        local TARGET FILE="$PKGMK_WORK_DIR/.tmp"
        
        cd $PKGMK_PACKAGE_DIR
        if [ -z $TARGETS ]; then
                get_packages_list
                if [ -z $TARGETS ]; then
                        error "Unable to update footprint"
                fi
        fi
        for TARGET in ${TARGETS[@]}; do
                if [ -f $TARGET ]; then
                        PKGMK_FOOTPRINT="$(get_package_name $TARGET)".footprint
                        make_footprint > $FILE.footprint
                        if [ -f $PKGMK_FOOTPRINT ]; then
                                sort -k 3 $PKGMK_FOOTPRINT > $FILE.footprint.orig
                                diff -w -t -U 0 $FILE.footprint.orig $FILE.footprint | \
                                        sed '/^@@/d' | \
                                        sed '/^+++/d' | \
                                        sed '/^---/d' | \
                                        sed 's/^+/NEW       /g' | \
                                        sed 's/^-/MISSING   /g' > $FILE.footprint.diff
                        if [ -s $FILE.footprint.diff ]; then
                                if [ "$PKGMK_IGNORE_NEW" = "yes" ]; then
                                        mv $FILE.footprint $PKGMK_FOOTPRINT
                                        warning "Footprint mismatch found:"
                                else
                                        error "Footprint mismatch found:"
                                        BUILD_SUCCESSFUL="no"
                                fi
                                cat $FILE.footprint.diff >&2
                        fi
                else
                        warning "Footprint not found, creating new."
                        mv $FILE.footprint $PKGMK_FOOTPRINT
                fi
                else
                        error "Package '$TARGET' was not found."
                        BUILD_SUCCESSFUL="no"
                fi
        done
}
make_work_dir() {
        export PKG="$PKGMK_WORK_DIR/pkg"
        export SRC="$PKGMK_WORK_DIR/src"
        pkgdir=$PKG
        srcdir=$SRC
        umask 022
        
        cd $PKGMK_ROOT
        [ "$PKGMK_PACK_ONLY" == "yes" ] ||  remove_work_dir
        mkdir -p $SRC $PKG

        if [ "$PKGMK_IGNORE_MD5SUM" = "no" ]; then
                check_md5sum
        fi
}
remove_work_dir() {
        rm -rf $PKGMK_WORK_DIR
}
pack_lib() {
        local DIR SUBDIR
        for DIR in usr opt/*; do
                for SUBDIR in lib lib64; do
                        if [ -d $DIR/$SUBDIR/pkgconfig ]; then
                                mv $DIR/$SUBDIR/pkgconfig $PKG
                        fi
                        if [ -d $DIR/$SUBDIR ]; then
                                bsdtar -r -f \
                                $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}${PKGMK_ARCH}.${PKGMK_PACKAGE_EXT} \
                                $DIR/$SUBDIR || BUILD_SUCCESSFUL="no"
                                rm -r $DIR/$SUBDIR
                                j=1
                        fi
                        if [ -d $PKG/pkgconfig ]; then
                                mkdir $DIR/$SUBDIR
                                mv $PKG/pkgconfig $DIR/$SUBDIR/pkgconfig
                        fi
                done
        done
}
remove_lib() {
        local DIR SUBDIR
        for DIR in usr opt/*; do
                for SUBDIR in lib lib64;do
                        if [ -d $DIR/$SUBDIR ]; then
                                rm -r $DIR/$SUBDIR
                        fi
                done
        done
}
pack_sources() {
        mkdir -p $PKG/${PKGMK_PACK_SOURCE_DIR}/${name}
        local FILE LOCAL_FILENAME LOCAL_PACK_SOURCE_DIR

        LOCAL_PACK_SOURCE_DIR="$(echo $PKGMK_PACK_SOURCE_DIR|sed 's@^/@@')"
        for FILE in ${source[@]}; do
                LOCAL_FILENAME="$(get_filename $FILE)"
                if [[ -f "$LOCAL_FILENAME" ]]; then
                        cp $LOCAL_FILENAME ${PKG}/${PKGMK_PACK_SOURCE_DIR}/${name}/
                else
                        cp $SRC/$LOCAL_FILENAME ${PKG}/${PKGMK_PACK_SOURCE_DIR}/${name}/
                fi
        done
        bsdtar -r -f \
        $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
        ${LOCAL_PACK_SOURCE_DIR} || BUILD_SUCCESSFUL="no"
}
remove_sources() {
        rm -r $PKG/${PKGMK_PACK_SOURCE_DIR}
}
pack_devel() {
        local DIR SUBDIR
        for DIR in usr opt/*; do
                for SUBDIR in include share/pkgconfig lib/cmake lib/qt6/mkspecs lib/pkgconfig lib64/pkgconfig; do
                        if [ -d $DIR/$SUBDIR ]; then
                                bsdtar -r -f \
                                $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
                                $DIR/$SUBDIR || BUILD_SUCCESSFUL="no"
                                rm -r $DIR/$SUBDIR
                                j=1
                                
                        fi
                done
        done
}
remove_devel() {
        local DIR SUBDIR
        for DIR in usr opt/*; do
                for SUBDIR in include share/pkgconfig lib/cmake lib/qt6/mkspecs lib/pkgconfig lib64/pkgconfig; do
                        if [ -d $DIR/$SUBDIR ]; then
                                rm -r $DIR/$SUBDIR
                        fi
                done
        done
}
pack_doc() {
        local DIR SUBDIR
        for DIR in usr/share opt/*/share; do
                for SUBDIR in doc gtk-doc; do
                        if [ -d $DIR/$SUBDIR ]; then
                                bsdtar -r -f \
                                $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
                                $DIR/$SUBDIR || BUILD_SUCCESSFUL="no"
                                j=1
                        fi
                done
        done
}
remove_doc() {
        local DIR SUBDIR
        for DIR in usr/share opt/*/share; do
                for SUBDIR in doc gtk-doc; do
                        if [ -d $DIR/$SUBDIR ]; then
                                rm -r $DIR/$SUBDIR
                        fi
                done
        done
}
pack_man() {
        local DIR SUBDIR
        for DIR in usr/share opt/*/share; do
                for SUBDIR in info man; do
                        if [ -d $DIR/$SUBDIR  ]; then
                                bsdtar -r -f \
                                $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
                                $DIR/$SUBDIR || BUILD_SUCCESSFUL="no"
                                j=1
                        fi
                done
        done
}
remove_man() {
        local DIR SUBDIR
        for DIR in usr/share opt/*/share; do
                for SUBDIR in info man; do
                        if [ -d $DIR/$SUBDIR  ]; then
                                rm -r $DIR/$SUBDIR
                        fi
                done
        done
}
pack_service() {
        local DIR="etc/rc.d"
        if [ -d $DIR ]; then
                bsdtar -r -f \
                $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
                $DIR || BUILD_SUCCESSFUL="no"
                j=1
        fi
}
remove_service() {
        local DIR="etc/rc.d"
        if [ -d $DIR ]; then
                rm -r $DIR
        fi
}
build() {
  if [ -z $build ]; then
    # debian package is standard
    if [ -f data.tar.xz ]; then
      bsdtar -xf data.tar.xz -C $PKG
    else
      cd $name-$version
      ./configure --prefix=/usr
      make
      make DESTDIR=$PKG install
    fi
  else
    case $build in
      meson)
        meson setup --prefix=/usr \
                    --libdir=/usr/lib \
                    $name-$version \
                    build

        ninja -C build
        DESTDIR=$PKG ninja -C build install;;

      autogen)
        cd $name-$version
        ./autogen.sh
        ./configure --prefix=/usr \
                    --disable-static
        make
        make DESTDIR=$PKG install;;

      autoreconf)
        cd $name-$version
        autoreconf -fiv
        ./configure --prefix=/usr \
                    --disable-static
        make
        make DESTDIR=$PKG install;;

      autotools)
        cd $name-$version
        ./configure --prefix=/usr \
                    --disable-static
        make
        make DESTDIR=$PKG install;;

      python3)
        cd ${_name}-$version
        python3 setup.py build
        python3 setup.py install --prefix=/usr --root=$PKG;;

      pip3)
        cd ${_name}-$version

        pip3 install --upgrade pip

        pip3 install wheel
        pip3 install setuptools

        pip3 wheel -w dist \
                --no-cache-dir \
                --no-build-isolation \
                --no-deps $PWD

        pip3 install --no-index \
                --no-user \
                --root=$PKG \
                --find-links dist \
                ${_name};;

      xorg)
        cd ${_name}-$version
        ./configure --prefix=/usr \
        --sysconfdir=/etc \
        --localstatedir=/var --disable-static
        make
        make DESTDIR=$PKG install;;

      kde5|cmake)
        cmake -B build \
              -S $name-$version \
              -DCMAKE_INSTALL_PREFIX=/usr \
              -DCMAKE_BUILD_TYPE=Release \
              -DCMAKE_INSTALL_LIBDIR=lib \
              -DBUILD_TESTING=OFF \
              -Wno-dev

        cmake --build build
        DESTDIR="$PKG" cmake --install build;;

      kde6)
        cmake -B build \
          -S $name-$version \
          -DCMAKE_INSTALL_PREFIX=/usr \
          -DCMAKE_BUILD_TYPE=Release \
          -DCMAKE_INSTALL_LIBDIR=lib \
          -DQT_MAJOR_VERSION=6 \
          -DBUILD_TESTING=OFF \
          -Wno-dev

        cmake --build build
        DESTDIR="$PKG" cmake --install build;;

      *)
        echo "$(basename $PKGMK_COMMAND): function build invalid build type"
        exit $E_BUILD;;
    esac
  fi
}

add_meta_files() {
        local NAME
        NAME="$(get_package_name $1)"
        cd $PKG
        if [ -f $PKGMK_ROOT/${NAME}.README ]; then
                cp $PKGMK_ROOT/${NAME}.README .README
        fi
        if [ -f $PKGMK_ROOT/${NAME}.pre-install ]; then
                cp $PKGMK_ROOT/${NAME}.pre-install .PRE
        fi
        if [ -f $PKGMK_ROOT/${NAME}.post-install ]; then
                cp $PKGMK_ROOT/${NAME}.post-install .POST
        fi
        for file in .README .PRE .POST; do
                if [ -f $file ]; then
                        bsdtar -r -f $PKGMK_PACKAGE_DIR/$1 \
                        $file
                        rm $file
                fi
        done
}
add_meta_to_archive() {
        local NAME TAR SIZE_I DESC URL CONT PACK MAINT ARCH GROUP
        NAME="$(get_package_name $1)"
        ARCH="$(get_package_arch $1)"
        GROUP="$(echo $NAME|cut -d "." -f2)"
        [ "$GROUP" == "$NAME" ] && unset GROUP
        info  "Adding meta data to Archive $NAME"

        TAR="$PKGMK_WORK_DIR/tar"
        mkdir -p $TAR
        cd $TAR
        DESC="none"
        URL="none"
        PACK="none"
        MAINT="none"
        CONT="none"

        bsdtar xf $PKGMK_PACKAGE_DIR/$1

        [ ! -z "$description" ] && DESC=$description
        [ ! -z "$packager" ] && PACK=$packager
        [ ! -z "$maintainer" ] && MAINT=$maintainer
        [ ! -z "$contributors" ] && CONT=$contributors
        [ ! -z "$url" ] && URL=$url

        SIZE_I="$(du -b $PKGMK_PACKAGE_DIR/$1|sed "s/\t/ /"|cut -d " " -f1)"

        echo "N$NAME" > .META
        echo "D$DESC" >> .META
        echo "U$URL" >> .META
        echo "C$CONT" >> .META
        echo "M$MAINT" >> .META
        echo "P$PACK" >> .META
        echo "S$SIZE_I" >> .META
        echo "V$version" >> .META
        echo "r$release" >> .META
        echo "B$PKGMK_BUILDVER" >> .META
        echo "a$ARCH" >> .META
        [ ! -z "$COLLECTION" ] && echo "c$COLLECTION" >> .META
        [ ! -z "$GROUP" ] && echo "g$GROUP" >> .META

        # TODO improve the pkginfo --runtimedepfiles functionnality
        if [ "$PKGMK_IGNORE_RUNTIMEDEPS" == "no" ];then
                for dep in $(pkginfo --runtimedepfiles $TAR|grep -v ^[A-Z]); do
                        # We don't want the package in the runtime dep list
                        if [[ $NAME != ${dep::-${#PKGMK_BUILDVER}} ]]; then
                                echo "R$dep" >> .META
                        fi
                done
                # We don't want any .devel dependencies here
                sed -i '/^R[[a-z0-9]*[-_+]*]*\.devel/d' .META
                if [ ! -z $run ] && [ "$NAME" == "$name" ]; then
                        info "Adding runtime deps to Archive $NAME"
                        for rd in ${run[@]}; do
                                if [ "$(echo $rd|cut -d "." -f1)" == "$name" ]; then
                                        echo "R$rd${PKGMK_BUILDVER}" >> .META
                                        continue
                                fi
                                if [ "$(pkginfo -b $rd)" == "0" ] || [ "$(pkginfo -b $rd)" == "" ]; then
                                        error "Runtime dependency $rd not found, cannot continue"
                                        clean
                                        exit $E_DEPS;
                                fi
                                echo "R$rd$(pkginfo -b $rd)" >> .META
                        done
                fi
                if [ ! -z "$GROUP" ]; then
                        rds=("run_$GROUP[@]")
                        if [ ! -z "$(echo ${!rds})" ]; then
                                info "Adding runtime deps to Archive $NAME"
                                for rd in $(echo ${!rds}); do
                                        if [ "$(pkginfo -b $rd)" == "0" ] || [ "$(pkginfo -b $rd)" == "" ]; then
                                                error "Runtime dependency $rd not found, cannot continue"
                                                clean
                                                exit $E_DEPS;
                                        fi
                                        echo "R$rd$(pkginfo -b $rd)" >> .META
                                done
                        fi
                fi
        fi
        if [ ! -z $alias ]; then
                info "Adding Alias to Archive $NAME"
                for al in ${alias[@]}; do
                        if [ -z "$GROUP" ];then
                                echo "A$al" >> .META
                        else
                                echo "A$al.$GROUP" >> .META
                        fi
                done
        fi
        if [ ! -z $set ]; then
                info "Adding Sets to Archive $NAME"
                for i in ${set[@]}; do
                        echo "s$i" >> .META

                done
        fi
        if [ ! -z "$categories" ]; then
                info "Adding Categories to Archive $NAME"
                for i in ${categories[@]}; do
                        echo "T$i" >> .META

                done
        fi
        bsdtar -tf $PKGMK_PACKAGE_DIR/$1 > .MTREE

        rm $PKGMK_PACKAGE_DIR/$1

        for file in .META .MTREE .README .PRE .POST .INFO; do
                if [ -f $file ]; then
                        bsdtar -r -f $PKGMK_PACKAGE_DIR/$1 \
                        $file
                fi
        done

        bsdtar -r -f $PKGMK_PACKAGE_DIR/$1 *

        cd ..
        rm -rf $TAR
}
compress_archive() {
        info "compress $1"
        case $PKGMK_COMPRESSION_MODE in
                gz) gzip -9 $1;;
                bz2) bzip2 -9  $1;;
                xz)  xz -z $PKGMK_COMPRESSION_OPTS $1;;
                zst) zstd $PKGMK_COMPRESSION_OPTS $1;;
        esac
}
build_package() {
        # TODO find a better solution for the hardcoded folders
        local BUILD_SUCCESSFUL="no"

        if [ ! -z $TARGETS ]; then
                for TARGET in ${TARGETS[@]}; do
                        warning "$TARGET already exist"
                done
        fi

        check_file "$TARGET"
        make_work_dir

        if [ "$UID" != "0" ]; then
                warning "Packages should be built as root."
        fi
        if [ "$PKGMK_PACK_ONLY" == "no" ];then
                info "Building starting..."
                unpack_source

                get_dependencies

                if [ ! -z $DEPENDENCIES ];then
                        for i in ${DEPENDENCIES[@]}; do
                                pkgadd $(echo $i|cut -d ":" -f2)
                        done
                fi

                cd $SRC

                if have_function 'prepare'; then
                        (set -e -x ; prepare)
                        if [ $? = 0 ]; then
                                BUILD_SUCCESSFUL="yes"
                        fi
                else
                        BUILD_SUCCESSFUL="yes"
                fi

                if [ "$BUILD_SUCCESSFUL" == "yes" ]; then
                        BUILD_SUCCESSFUL="no"
                        (set -e -x ; build)
                        if [ $? = 0 ]; then
                                BUILD_SUCCESSFUL="yes"
                        fi
                fi
                if [ "$BUILD_SUCCESSFUL" == "yes" ]; then
                        if have_function 'package'; then
                                BUILD_SUCCESSFUL="no"
                                (set -e -x ; package)
                                if [ $? = 0 ]; then
                                        BUILD_SUCCESSFUL="yes"
                                fi
                        fi
                fi
        else
                BUILD_SUCCESSFUL="yes"
        fi
        if [ "$BUILD_SUCCESSFUL" == "yes" ]; then

                remove_dependencies

                if [ "$PKGMK_NO_STRIP" = "no" ]; then
                        strip_files
                fi
                
                compress_manpages

                cd $PKG
                i=0
                j=0

                PKGMK_BUILDVER="$(date +%s)"

                clean_old_binaries

                for group in ${PKGMK_GROUPS[@]}; do
                        if [ "$(type -t ${group})" == "function" ]; then
                                BUILD_SUCCESSFUL="no"
                                (set -e -x ;${group})
                                if [ $? = 0 ]; then
                                        j=1
                                        BUILD_SUCCESSFUL="yes"
                                else
                                        remove_dependencies
                                        error "Building '$TARGET' failed."
                                        exit $E_BUILD
                                fi
                        else
                                if [ "$1" != "$TARGET" ]; then
                                        if [ -f $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}* ]; then
                                                rm $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}*
                                        fi
                                        pack_${group}
                                fi
                                remove_${group}
                                if [ "$BUILD_SUCCESSFUL"  == "no" ]; then
                                        remove_dependencies
                                        error "Building '$TARGET' failed."
                                        exit $E_BUILD
                                fi
                        fi
                        # Add the meta in each package
                        if [ $j == 1 ]; then
                                if [ "$BUILD_SUCCESSFUL"  == "no" ]; then
                                        remove_dependencies
                                        error "Building '$TARGET' failed."
                                        exit $E_BUILD
                                fi
                                j=0
                        fi
                        j=0
                done
                if [ "$PKGMK_SPLIT_LOCALES" = "yes" ]; then
                        for group in ${PKGMK_LOCALES[@]}; do
                                if [ "$(type -t locale_${group})" == "function" ]; then
                                        BUILD_SUCCESSFUL="no"
                                        (set -e -x ;locale_${group})
                                        j=1
                                        if [ $? = 0 ]; then
                                                BUILD_SUCCESSFUL="yes"
                                        else
                                                remove_dependencies
                                                error "Building '$TARGET' failed."
                                                exit $E_BUILD
                                        fi
                                else
                                        for DIR in usr/share/locale/${group}* opt/*/share/locale/${group}*; do
                                                if [ -d $DIR ]; then
                                                        if [ "$1" != "$TARGET" ]; then
                                                                bsdtar -r -f $PKGMK_PACKAGE_DIR/${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT} \
                                                                                        $DIR || abort_on_build "${name}.${group}${PKGMK_BUILDVER}any.${PKGMK_PACKAGE_EXT}"
                                                        fi
                                                        rm -rf $DIR
                                                fi
                                        done
                                fi
                        done
                        for DIR in usr/share/locale opt/*/share/locale; do
                                if [ -d $DIR ]; then
                                        rm -rf $DIR
                                fi
                        done
                fi
                check_build
                if [ "$(ls|wc -l)" != "0" ]; then
                        bsdtar -r -f $PKGMK_PACKAGE_DIR/${name}${PKGMK_BUILDVER}${PKGMK_ARCH}.${PKGMK_PACKAGE_EXT} *
                else
                        remove_dependencies
                        error "No files found in $PKG..."
                        exit $E_BUILD
                fi
                if [ $? = 0 ]; then
                        BUILD_SUCCESSFUL="yes"
                        if [ "$PKGMK_IGNORE_FOOTPRINT" == "yes" ]; then
                                warning "Footprint ignored."
                        else
                                check_footprint
                        fi
                fi
        fi

        get_packages_list

        if [ "$BUILD_SUCCESSFUL" = "yes" ]; then
                if [ "$1" != "$TARGET" ]; then
                        if [ -z $categories ]; then
                                info "trying to get categories..."
                                get_categories
                        fi
                        get_packages_list
                        if [ -z $TARGETS ]; then
                                        remove_dependencies
                                        error "Package(s) not found(s)..."
                                        exit $E_BUILD
                        fi
                        for TARGET in ${TARGETS[@]}; do
                                add_meta_files $TARGET
                                add_meta_to_archive $TARGET
                        done
                else
                        add_meta_files "$(basename $TARGET)"
                        add_meta_to_archive "$(basename $TARGET)"
                fi
                if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                        remove_work_dir
                fi
        else
                if [ -f $TARGET ]; then
                        touch -r $PKGMK_ROOT/$PKGMK_PKGFILE $TARGET &> /dev/null
                fi
                remove_dependencies
                error "Building '$TARGET' failed."
                exit $E_BUILD
        fi
}
install_package() {
        local COMMAND
        get_packages_list
        if [ -z $TARGETS ]; then
                error "Package(s) not found(s) nothing to install..."
                exit $E_INSTALL
        fi
        for TARGET in ${TARGETS[@]}; do
                if [ "$(get_package_arch $TARGET)" = "$PKGMK_ARCH" ] || [ "$(get_package_arch $TARGET)" = "any" ]; then
                        info "Installing '$TARGET'."
        
                        if [ "$PKGMK_INSTALL" = "install" ]; then
                                COMMAND="pkgadd $PKGMK_PACKAGE_DIR/$TARGET"
                        else
                                COMMAND="pkgadd -u $PKGMK_PACKAGE_DIR/$TARGET"
                        fi
        
                        cd $PKGMK_ROOT
                        echo "$COMMAND"
                        $COMMAND
        
                        if [ $? = 0 ]; then
                                info "Installing '$TARGET' succeeded."
                        else
                                error "Installing '$TARGET' failed."
                                exit $E_INSTALL
                        fi
                fi
        done
}
recursive() {
        local ARGS FILE DIR
        [ -f $PKGMK_REPO ] && rm -v $PKGMK_REPO
        
        ARGS="$(echo "$@" | sed -e "s/--recursive//g" -e "s/-r//g")"

        for FILE in $(find $PKGMK_ROOT -name $PKGMK_PKGFILE | sort); do
                DIR="$(dirname $FILE)/"
                if [ -d $DIR ]; then
                        [ "$PKGMK_QUIET" = "yes" ] || \
                        info "Entering directory '$DIR'."
                        (cd $DIR && $PKGMK_COMMAND $ARGS)
                        [ "$PKGMK_QUIET" = "yes" ] || \
                        info "Leaving directory '$DIR'."
                fi
        done
}
clean() {
        unset TARGETS
        get_packages_list
        if [ ! -z $TARGETS ]; then
                for TARGET in ${TARGETS[@]}; do
                        if [ -f $PKGMK_PACKAGE_DIR/$TARGET ]; then
                                info "Removing $TARGET"
                                rm -f $PKGMK_PACKAGE_DIR/$TARGET
                        fi
                done
        else
                warning "$TARGETS not found"
        fi      
        find $PKGMK_ROOT -name "*.md5sum" -exec rm -v {} \; 2> /dev/null
        find $PKGMK_ROOT -name "*.footprint" -exec rm -v {} \; 2> /dev/null
        unset TARGETS
}
update_footprint() {
        cd $PKGMK_PACKAGE_DIR
        if [ -z $TARGETS ]; then
                get_packages_list
                if [ -z $TARGETS ]; then                
                        error "Package(s) not found(s), unable to update footprint."
                        exit $E_FOOTPRINT
                fi
        fi
        for TARGET in ${TARGETS[@]}; do
                if [ ! -f $TARGET ]; then
                        error "Unable to update footprint. File '$TARGET' not found."
                        exit $E_FOOTPRINT
                fi
                PKGMK_FOOTPRINT="$(get_package_name $TARGET)".footprint
                check_file "$PKGMK_FOOTPRINT"
                make_footprint > $PKGMK_FOOTPRINT
                touch $TARGET

                info "Footprint updated for $TARGET"
        done
}
build_needed() {
        local FILE RESULT
        RESULT="yes"
        if [ -f $TARGET ]; then 
                RESULT="no"
                FILE="$(get_filename $PKGMK_PKGFILE)"
                if [ ! -e $FILE ] || [ ! $TARGET -nt $FILE ]; then
                        RESULT="yes"
                fi
        fi
        echo $RESULT
}
update_collection_repo() {
        local HEAD BUILD_DATE VERSION RELEASE DESCRIPTION URL MAINTAINER PACKAGER EXTENSION \
ALIAS GROUP CONTRIBUTORS SET CATEGORY
        if [ -f $PKGMK_REPO ]; then
                for i in $(tail -n +2  $PKGMK_REPO)
                do
                        if [ "$(echo $i|cut -d "#" -f3)" != "" ];then
                                for j in ${PKGMK_GROUPS[@]}; do
                                        k=$(echo $i|cut -d "#" -f2|cut -d "." -f2)
                                        [ $k == $j ] && \
                                        GROUP="$k $GROUP"
                                done
                        fi
                done
                HEAD="$(head -1 $PKGMK_REPO)"
                if [ "${HEAD:10:1}" = "#" ]; then
                        BUILD_DATE="$(echo $HEAD|cut -d "#" -f1)"
                        EXTENSION="$(echo $HEAD|cut -d "#" -f2)"
                        VERSION="$(echo $HEAD|cut -d "#" -f3)"
                        RELEASE="$(echo $HEAD|cut -d "#" -f4)"
                        DESCRIPTION="$(echo $HEAD|cut -d "#" -f5)"
                        URL="$(echo $HEAD|cut -d "#" -f6)"
                        MAINTAINER="$(echo $HEAD|cut -d "#" -f7)"
                        PACKAGER="$(echo $HEAD|cut -d "#" -f8)"
                        CONTRIBUTORS="$(echo $HEAD|cut -d "#" -f9)"
                        if [ ! -z "$categories" ]; then
                                for i in ${categories[@]}
                                do
                                        CATEGORY="$i $CATEGORY"
                                done
                        else
                                CATEGORY=""
                        fi
                        if [ ! -z $set ]; then
                                for i in ${set[@]}
                                do
                                        SET="$i $SET"
                                done
                        else
                                SET=""
                        fi
                        if [ ! -z $alias ]; then
                                for i in ${alias[@]}
                                do
                                        ALIAS="$i $ALIAS"
                                done
                        else
                                ALIAS=""
                        fi

                        [ -f ../$PKGMK_REPO ] && sed -i "/#$name#/d" ../$PKGMK_REPO
                        echo \
"$(md5sum $PKGMK_REPO|cut -d " " -f1)#$BUILD_DATE#$name#$VERSION#$RELEASE#$DESCRIPTION#$URL#$MAINTAINER#$PACKAGER#$EXTENSION#$ALIAS#$GROUP#$CONTRIBUTORS#$SET#$CATEGORY" \
                        >> ../$PKGMK_REPO
                fi
        fi
}
update_repo() {
        local FILE MD5SUM EXT
        cd $PKGMK_ROOT
        get_packages_list
        if [ -z "$description"  ]; then
                description="n.a."
        else
                if [ ! -z "$(echo $description|grep \#)" ]; then
                        description=$(echo $description|sed "s@#@\\\#@g")
                fi
        fi
        [ -z "$url"  ] && url="n.a."
        [ -z "$maintainer"  ] && maintainer="n.a."
        [ -z "$packager"  ] && packager="n.a."
        [ -z "$contributors" ] && contributors="none"
        [ -z "$set" ] && set="none"
        [ -z "$categories" ] && categories="none"
        if [ ! -z $TARGETS ]; then
                EXT="$(get_package_extension $TARGETS)"
                echo -n "$(get_package_builddate $TARGETS)#$EXT#$version#$release" > $PKGMK_REPO
                echo -n "#$description#$url#$maintainer#$packager#$contributors#$set#$categories" >> .PKGREPO
                printf "\n" >> .PKGREPO
                for TARGET in ${TARGETS[@]}; do
                        FILE="$(basename $TARGET)"
                        MD5SUM="$(md5sum $TARGET|cut -d " " -f1)"
                        echo "$MD5SUM#$(get_package_name $TARGET)#$(get_package_arch $TARGET|sed "s/-//")" >> $PKGMK_REPO
                done
                for FILE in  ${name}*
                do
                        [ ! -f "$FILE" ] && break
                        if [ "${FILE:$((${#FILE} - ${#EXT} ))}" != "$EXT" ]; then
                                echo "$(md5sum $FILE|cut -d " " -f1)#$FILE" >> $PKGMK_REPO
                        fi
                done
        else
                rm -f $PKGMK_REPO
        fi
        echo "$(md5sum $PKGMK_PKGFILE|cut -d " " -f1)#$PKGMK_PKGFILE" >> $PKGMK_REPO
}
clean_repo_files() {
        cd $PKGMK_ROOT
        if [ -f $PKGMK_REPO ]; then
                rm $PKGMK_REPO
                info "Removing $PKGMK_REPO"
        fi
}
check_sources_uptodate() {
        local newversion
        if  [ ! "$(which lynx)" ]; then
                error "lynx is not installed, cannot check sources"
                exit 0
        fi
        if [ "$(type -t uptodate)" == "function" ]; then
                newversion="$(uptodate)"
                if [ "$newversion" != "$version" ]; then
                        info "$name: $version -> $newversion"
                fi
        fi
}
check_build() {
        local LIST
        if [ "$PKGMK_KEEP_LA_LIBS" != "yes" ]; then
                find $PKG ! -type d -name "*.la" -exec rm -fv -- '{}' +
        fi
        LIST="$(find $PKG ! -type d -name "*.la")"
        if [ ! -z "$LIST" ]; then
                warning "following files should be removed:"
                for i in $LIST
                do
                        echo $i
                done
        fi
}
clean_old_binaries() {

        local TARGET
        get_packages_list
        for TARGET in ${TARGETS[@]}; do
                rm $PKGMK_PACKAGE_DIR/$TARGET
        done
}
interrupted() {
        echo ""
        error "Interrupted."

        if [ "$PKGMK_KEEP_WORK" = "no" ]; then
                rm -rf $PKGMK_WORK_DIR
        fi

        exit $E_GENERAL
}
print_help() {
        echo "usage: $(basename $PKGMK_COMMAND) [options]"
        echo "options:"
        echo "   -i,  --install             build and install packages"
        echo "   -u,  --upgrade             build and install packages (as upgrade)"
        echo "   -r,  --recursive           search for and build packages recursively"
        echo "   -d,  --download            download missing source file(s)"
        echo "   -f,  --force               build packages even if they appear to be up-to-date"
        echo "   -q,  --quiet               do not printout messages, usefull for -itd option"
        echo "  -cs,  --check-syntax        do not build, only check package syntax"
        echo "  -cv,  --show-version-only   do not build, only show the version of the port and exit"
        echo "  -td,  --test-download       do not build, do not download, but test source file(s)"
        echo "  -do,  --download-only       do not build, only download missing source file(s)"
        echo "  -eo,  --extract-only        do not build, only extract source file(s)"
        echo " -itd,  --is-up-to-date       check if the version is the same as upstream sources"
        echo " -utd,  --up-to-date          do not build, only check if package is up to date"
        echo "  -ur,  --update-repo         do not build, only update the $PKGMK_REPO file"
        echo "  -uc,  --update-collection   do not build, only update the $PKGMK_REPO file of the collection means from the parent directory"
        echo "  -uf,  --update-footprint    update footprint using the result of the last build"
        echo "  -ic,  --ignore-collection   build packages without update the $PKGMK_REPO file of the collection means from the parent directory"
        echo "  -if,  --ignore-footprint    build packages without checking footprint"
        echo "  -in,  --ignore-new          build packages, ignore new files in a footprint missmatch"
        echo "  -um,  --update-md5sum       update md5sum"
        echo "  -im,  --ignore-md5sum       build packages without checking md5sum"
        echo "  -cm,  --check-md5sum        do not build, only check md5sum"
        echo "  -ns,  --no-strip            do not strip executable binaries or libraries"
        echo "  -co,  --clean-only          do not build, only remove packages and downloaded files, update $PKGMK_REPO file"
        echo "  -kw,  --keep-work           keep temporary working directory"
        echo "  -po,  --pack-only           do not compile the sources, pack only what's in the \$PKG directory, means the build is done manually"
        echo "  -cf,  --config-file <file>  use alternative configuration file"
        echo "  -v,   --version             print version and exit "
        echo "  -h,   --help                print help and exit"
}
parse_options() {
        while [ "$1" ]; do
                case $1 in
                        -i|--install)
                                PKGMK_INSTALL="install" ;;
                        -u|--upgrade)
                                PKGMK_INSTALL="upgrade" ;;
                        -r|--recursive)
                                PKGMK_RECURSIVE="yes" ;;
                        -q|--quiet)
                                PKGMK_QUIET="yes" ;;
                        -td|--test-download)
                                PKGMK_TEST_DOWNLOAD_ONLY="yes" ;;
                        -d|--download)
                                PKGMK_DOWNLOAD="yes" ;;
                        -cs|--check-syntax)
                                PKGMK_CHECK_SYNTAX="yes" ;;
                        -do|--download-only)
                                PKGMK_DOWNLOAD="yes"
                                PKGMK_DOWNLOAD_ONLY="yes" ;;
                        --debug)
                                PKGMK_DEBUG="yes" ;;
                        -eo|--extract-only)
                                PKGMK_EXTRACT_ONLY="yes" ;;
                        -cv|--show-version-only)
                                PKGMK_SHOW_VERSION="yes";;
                        -itd|--is-up-to-date)
                                PKGMK_SOURCES_UP_TO_DATE="yes";;
                        -utd|--up-to-date)
                                PKGMK_UP_TO_DATE="yes" ;;
                        -uc|--update-collection)
                                PKGMK_UPDATE_COLLECTION="yes";;
                        -ur|--update-repo)
                                PKGMK_UPDATE_REPO="yes" ;;
                        -uf|--update-footprint)
                                PKGMK_UPDATE_FOOTPRINT="yes" ;;
                        -if|--ignore-footprint)
                                PKGMK_IGNORE_FOOTPRINT="yes" ;;
                        -in|--ignore-new)
                                PKGMK_IGNORE_NEW="yes" ;;
                        -um|--update-md5sum)
                                PKGMK_UPDATE_MD5SUM="yes" ;;
                        -im|--ignore-md5sum)
                                PKGMK_IGNORE_MD5SUM="yes" ;;
                        -ic|--ignore-collection)
                                PKGMK_IGNORE_COLLECTION="yes" ;;
                        -ir|--ignore-repo)
                                PKGMK_IGNORE_REPO="yes" ;;
                        -cm|--check-md5sum)
                                PKGMK_CHECK_MD5SUM="yes" ;;
                        -ns|--no-strip)
                                PKGMK_NO_STRIP="yes" ;;
                        -f|--force)
                                PKGMK_FORCE="yes" ;;
                        -co|--clean-only)
                                PKGMK_CLEAN_ONLY="yes" ;;
                        -kw|--keep-work)
                                PKGMK_KEEP_WORK="yes" ;;
                        -po|--pack-only)
                                PKGMK_KEEP_WORK="yes"
                                PKGMK_PACK_ONLY="yes" ;;
                        -cf|--config-file)
                                if [ ! "$2" ]; then
                                        echo "$(basename $PKGMK_COMMAND): option $1 requires an argument"
                                        exit 1
                                fi
                                PKGMK_CONFFILE="$2"
                                shift ;;
                        -v|--version)
                                echo "$(basename $PKGMK_COMMAND) (cards) $PKGMK_VERSION"
                                exit 0 ;;
                        -h|--help)
                                print_help
                                exit 0 ;;
                        *)
                                echo "$(basename $PKGMK_COMMAND): invalid option $1"
                                exit 1 ;;
                esac
                shift
        done
}
main() {
        if [ -f /etc/profile ]; then
                source /etc/profile
        fi

        parse_options "$@"

        if [ ! -f $PKGMK_CONFFILE ]; then
                error "File '$PKGMK_CONFFILE' not found."
                exit $E_GENERAL
        fi

        . $PKGMK_CONFFILE

        if ! ( $(which pkginfo > /dev/null) ); then
                warning "pkginfo NOT FOUND, footprint ignored."
                PKGMK_IGNORE_FOOTPRINT="yes"
                warning "pkginfo NOT FOUND, runtime dependencies ignored."
                PKGMK_IGNORE_RUNTIMEDEPS="yes"
        fi

        local FILE TARGET DIR NAME

        release=1
        NAME="$(basename $PKGMK_ROOT)"

        DIR="$(dirname $PKGMK_ROOT)"
        COLLECTION="$(basename $DIR)"
        if [ -z $PKGMK_COLLECTIONS ]; then
                if [ $COLLECTION != "base" ]; then
                        case $COLLECTION in
                                cli)
                                        PKGMK_COLLECTIONS="base cli";;
                                cli-extra)
                                        PKGMK_COLLECTIONS="base cli cli-extra";;
                                gui)
                                        PKGMK_COLLECTIONS="base cli gui";;
                                *)
                                        PKGMK_COLLECTIONS="base cli gui $COLLECTION";;
                        esac
                else
                        PKGMK_COLLECTIONS=($COLLECTION)
                fi
        fi
        if [ "$PKGMK_RECURSIVE" = "yes" ]; then
                recursive "$@"
                exit 0
        fi
        [ ! -f $PKGMK_PKGFILE  ] && PKGMK_PKGFILE="PKGBUILD"

        if [ ! -f $PKGMK_PKGFILE  ]; then
                error "File '$PKGMK_PKGFILE' not found."
                exit $E_PKGFILE
        fi
        # Need to be set so that we can use the 'name' variable
        name=$NAME
        [ -f ../$PKGMK_EXTRA_CONFFILE ] && . ../$PKGMK_EXTRA_CONFFILE
        # Need to reset
        unset name
        . $PKGMK_PKGFILE

        if [ ! -z $pkgname ];then
                        name=$pkgname
        fi

        # If variable name still not define we take the directory name
        [ -z $name ] && name=$NAME
        if [ "$name" != "$NAME" ]; then
                error "Variable 'name' not the same as port name (directory)."
                exit $E_PKGFILE
        fi

        [ ! -z $pkgver ] && version=$pkgver
        [ ! -z $pkgrel ] && release=$pkgrel
        [ ! -z "$pkgdesc" ] && description=$pkgdesc

        if [ -z "$version" ]; then
                warning "Variable 'version' not initiated or not found in $PKGMK_PKGFILE."
        fi
        if [ "$PKGMK_SHOW_VERSION" == "yes" ]; then
                echo "$version"
                exit 0
        fi
        PKGMK_MD5SUM="$name.$(uname -m).md5sum"
        check_directory "$PKGMK_SOURCE_DIR"
        check_directory "$PKGMK_PACKAGE_DIR"
        check_directory "$(dirname $PKGMK_WORK_DIR)"

        check_pkgfile

        case $PKGMK_COMPRESSION_MODE in
                gz|bz2|xz|zst)
                        TARGET="$PKGMK_PACKAGE_DIR/${name}$PKGMK_BUILDVER$PKGMK_ARCH.$PKGMK_PACKAGE_EXT.$PKGMK_COMPRESSION_MODE"
                        ;;
                *)
                        error "Compression mode '$PKGMK_COMPRESSION_MODE' not supported"
                        exit $E_GENERAL
                        ;;
        esac
        case $PKGMK_COMPRESSION_MODE in
                gz)  COMPRESSION="-z" ;;
                bz2) export COMPRESSION="-j" ;;
                xz)  export COMPRESSION="-J" ;;
                zst) export COMPRESSION="--zstd" ;;
        esac

        if [ "$PKGMK_CLEAN_ONLY" = "yes" ]; then
                clean
                remove_source
                update_repo
                exit 0
        fi
        if [ "$PKGMK_UPDATE_COLLECTION" = "yes" ]; then
                update_collection_repo
                exit 0
        fi
        if [ "$PKGMK_UPDATE_REPO" = "yes" ]; then
                update_repo
                exit 0
        fi
        if [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ]; then
                update_footprint
                exit 0
        fi
        if [ "$PKGMK_CHECK_SYNTAX" = "yes" ]; then
                info "description: $description"
                info "url: $url"
                info "packager: $packager"
                info "maintainer: $maintainer"
                info "contributors: $contributors"
                info "collection: $COLLECTION"
                info "collections: $PKGMK_COLLECTIONS"
                get_dependencies
                [ -z $DEPENDENCIES ] && exit 0
                info "pkgmk dependencies:"
                for i in ${DEPENDENCIES[@]};do
                        echo "$i"
                done
                exit 0
        fi
        
        if [ "$PKGMK_UPDATE_MD5SUM" = "yes" ]; then
                download_source
                check_file "$PKGMK_MD5SUM"
                make_md5sum > $PKGMK_MD5SUM
                info "Md5sum updated."
                exit 0
        fi

        if [ "$PKGMK_DOWNLOAD_ONLY" = "yes" ]; then
                download_source
                exit 0
        fi
        
        if [ "$PKGMK_TEST_DOWNLOAD_ONLY" = "yes" ]; then
                test_download_sources
                exit 0
        fi

        if [ "$PKGMK_EXTRACT_ONLY" = "yes" ]; then
                download_source
                make_work_dir
                info "Extracting sources of package '${name}-$version'."
                unpack_source
                exit 0
        fi
        if [ "$PKGMK_SOURCES_UP_TO_DATE" = "yes" ]; then
                check_sources_uptodate
                exit 0
        fi
        # Print all informations
        info "PKGMK_INSTALL: $PKGMK_INSTALL"
        info "PKGMK_WORK_DIR: $PKGMK_WORK_DIR"
        info "PKGMK_SOURCE_DIR: $PKGMK_SOURCE_DIR"

        if [ "$PKGMK_INSTALL" = "no" ]; then
                PKGMK_CLEAN="no"
                warning "CLEAN IGNORED"
        else
                info "PKGMK_KEEP_SOURCES: $PKGMK_KEEP_SOURCES"
                info "PKGMK_CLEAN: $PKGMK_CLEAN"
        fi

        if [ "$PKGMK_IGNORE_REPO" != "yes" ]; then
                info "PKGMK_IGNORE_REPO: $PKGMK_IGNORE_REPO"
        else
                info "$PKGMK_REPO file will be deleted"
        fi
        if [ "$PKGMK_UPDATE_REPO" != "yes" ]; then
                info "PKGMK_UPDATE_REPO: $PKGMK_UPDATE_REPO"
        fi

        info "PKGMK_IGNORE_FOOTPRINT: $PKGMK_IGNORE_FOOTPRINT"
        info "PKGMK_IGNORE_MD5SUM: $PKGMK_IGNORE_MD5SUM"

        info "PKGMK_COMPRESS_PACKAGE: $PKGMK_COMPRESS_PACKAGE"
        if [ "$PKGMK_COMPRESS_PACKAGE" != "no" ]; then
                info "PKGMK_COMPRESSION_MODE: $PKGMK_COMPRESSION_MODE"
        fi
        info "name: ${name}"
        if [ ! -z "$version" ]; then
                info "version: $version"
        fi
        if [ ! -z "$release" ]; then
                info "release: $release"
        fi


        if [ "$PKGMK_UP_TO_DATE" = "yes" ]; then
                get_packages_list
                if [ ! -z $TARGETS ]; then
                        for TARGET in ${TARGETS[@]}; do
                                if [ "$(build_needed)" = "yes" ]; then
                                        info "Package '$TARGET' is not up to date."
                                else
                                        info "Package '$TARGET' is up to date."
                                fi
                        done
                else
                        if [ "$(build_needed)" = "yes" ]; then
                                info "Package '$TARGET' is not up to date."
                        else
                                info "Package '$TARGET' is up to date."
                        fi
                fi
                exit 0
        fi

        BUILD_NEEDED="no"

        # From here on check the base package not any group
        get_packages_list
        if [ -z $TARGETS ]; then
                TARGET="$PKGMK_PACKAGE_DIR/${name}$PKGMK_BUILDVER$PKGMK_ARCH.$PKGMK_PACKAGE_EXT.$PKGMK_COMPRESSION_MODE"
                if [ "$(build_needed)" = "no" ] && [ "$PKGMK_FORCE" = "no" ] && [ "$PKGMK_CHECK_MD5SUM" = "no" ]; then
                        info "Package '$TARGET' is up to date."
                else
                        [ "$PKGMK_PACK_ONLY" == "yes" ] ||  download_source
                        BUILD_NEEDED="yes"
                fi
        else
                FOUND=false
                for TARGET in ${TARGETS[@]}; do
                        if [ "$(build_needed)" = "no" ] && [ "$PKGMK_FORCE" = "no" ] && [ "$PKGMK_CHECK_MD5SUM" = "no" ]; then
                                if [ "$(get_package_arch $TARGET)" = "$PKGMK_ARCH" ]; then
                                        FOUND=true
                                fi
                                info "Package '$TARGET' is up to date."
                        else
                                download_source
                                BUILD_NEEDED="yes"
                                break
                        fi
                done
        fi
        if [ ! -z $FOUND ] ; then
                if [ $FOUND != true ];  then
                        download_source
                        TARGET=$PKGMK_PACKAGE_DIR/${name}$PKGMK_BUILDVER$PKGMK_ARCH.$PKGMK_PACKAGE_EXT
                        BUILD_NEEDED="yes"
                fi
        fi

        [ "$BUILD_NEEDED" == "yes" ] && build_package

        if [ "$PKGMK_INSTALL" != "no" ]; then
                install_package
        fi
        if [ "$PKGMK_COMPRESS_PACKAGE" = "yes" ]; then
                cd $PKGMK_PACKAGE_DIR
                get_packages_list
                for TARGET in ${TARGETS[@]}; do
                        if [  "${TARGET:$((${#TARGET} - ${#PKGMK_PACKAGE_EXT} ))}" == "$PKGMK_PACKAGE_EXT" ]; then
                                compress_archive $TARGET
                        fi
                done
        fi
        if [ "$PKGMK_KEEP_SOURCES" != "yes" ]; then
                remove_source
        fi
        if [ "$PKGMK_IGNORE_REPO" != "yes" ]; then
                update_repo
        fi
        if [ "$PKGMK_IGNORE_COLLECTION" != "yes" ]; then
                update_collection_repo
        fi
        if [ "$PKGMK_CLEAN" != "no" ]; then
                clean
                unset TARGETS
                if [ "$PKGMK_IGNORE_REPO" == "yes" ];then
                        clean_repo_files
                fi
        fi
        exit 0
}

trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM

export LC_ALL=POSIX

readonly PKGMK_VERSION="2.6.4"
readonly PKGMK_COMMAND="$0"
readonly PKGMK_ROOT="$PWD"
readonly PKGMK_PACKAGE_DIR="$PWD"

PKGMK_BUILDVER="$(date +%s)"

PKGMK_PACKAGE_EXT="cards.tar"

PKGMK_REPO=".PKGREPO"
PKGMK_ARCH="$(uname -m)"


PKGMK_CONFFILE="/etc/pkgmk.conf"
PKGMK_PKGFILE="Pkgfile"
PKGMK_EXTRA_CONFFILE=".pkgmk.conf"
PKGMK_MD5SUM="$name.$(uname -m).md5sum"
PKGMK_NOSTRIP=".nostrip"

PKGMK_KEEP_SOURCES="no"
PKGMK_CLEAN="yes"


PKGMK_GROUPS=()
PKGMK_LOCALES=()
PKGMK_SPLIT_LOCALES="yes"

PKGMK_SOURCE_MIRRORS=()
PKGMK_SOURCE_DIR="$PWD"
PKGMK_PACK_SOURCE_DIR="/usr/src"
PKGMK_WORK_DIR="$PWD/work"
PKGMK_DOWNLOAD="no"
PKGMK_IGNORE_FOOTPRINT="yes"
PKGMK_IGNORE_NEW="yes"
PKGMK_IGNORE_MD5SUM="yes"
PKGMK_IGNORE_REPO="yes"
PKGMK_IGNORE_COLLECTION="no"
PKGMK_IGNORE_RUNTIMEDEPS="no"

PKGMK_NO_STRIP="no"

PKGMK_COMPRESSION_MODE="xz"
PKGMK_COMPRESSION_OPTS="-9 --threads=$(getconf _NPROCESSORS_ONLN)"
[ "$PKGMK_ARCH"=="i686" ] && PKGMK_COMPRESSION_OPTS="-9"

PKGMK_COMPRESS_PACKAGE="no"

PKGMK_INSTALL="no"
PKGMK_RECURSIVE="no"

PKGMK_TEST_DOWNLOAD_ONLY="no"
PKGMK_DOWNLOAD_ONLY="no"
PKGMK_EXTRACT_ONLY="no"
PKGMK_IGNORE_UNPACK="no"
PKGMK_UP_TO_DATE="no"
PKGMK_UPDATE_FOOTPRINT="no"
PKGMK_FORCE="no"
PKGMK_DEBUG="no"
PKGMK_KEEP_WORK="no"
PKGMK_PACK_ONLY="no"
PKGMK_KEEP_LA_LIBS="no"

PKGMK_UPDATE_MD5SUM="no"
PKGMK_CHECK_MD5SUM="no"

main "$@"

# End of file
