#!bash
# vim:ff=unix:enc=utf8:ts=3:sw=3:et

# src2pkg-ng - package creation toolkit
# Copyright (C) 2005-2009 Gilbert Ashley
# Copyright (C) 2009 Timothy Goya

# src2pkg-ng is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2 as 
# published by the Free Software Foundation

# src2pkg-ng 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 src2pkg-ng.  If not, see <http://www.gnu.org/licenses/>.

{
   shopt -s nullglob
   declare HANDLER
   for HANDLER in "$SRC2PKG_NG_DIR/download"/* "$SRC2PKG_NG_DIR/repo"/* ; do
      source $HANDLER
   done
   shopt -u nullglob
}

make_dir() {
   declare TAG="$1"
   if [[ ! "$BUILDS_DIR" || "$BUILDS_DIR" == "/" ]] ; then
      tprintf $"@error{Error!}: BUILDS_DIR not valid\n"
      cleanup 1
   fi
   declare DIR="$BUILDS_DIR/$NAME-$VERSION-$TAG-$BUILD"
   if [[ -e "$DIR" ]] ; then
      tprintf $"@summary{Removing existing} %s\n" "${DIR##*/}"
      rm -rf "$DIR"
   fi
   tprintf $"@summary{Creating working directory} %s\n" "${DIR##*/}"
   mkdir -p "$DIR"
   add_cleanup_cmd 2 "rm -rf \"$DIR\""
   RET_DIR="$DIR"
}

fetch_src() {
   declare URL="$1"
   declare DIR="${2:-"."}"
   declare SOURCE_NAME="${URL##*/}"
   declare SOURCE="$SOURCES_DIR/$DIR/$SOURCE_NAME"
   if [[ -L "$SOURCE" ]] ; then
      SOURCE="$(readlink -f "$SOURCE")"
      tprintf $"@summary{Found symlink to source}: %s\n" "$SOURCE"
   elif [[ -e "$SOURCE" ]] ; then
      tprintf $"@summary{Found source}: %s\n" "$SOURCE"
   else
      tprintf $"@summary{Downloading from}: %s\n" "$URL"
      mkdir -p "$SOURCES_DIR/$DIR"
      declare PROTOCOL="${URL%%:*}"
      rm -f "$SOURCE.part"
      if download_$PROTOCOL "$URL" > "$SOURCE.part" ; then
         mv -f "$SOURCE.part" "$SOURCE"
         tprintf $"@summary{Downloaded source to}: %s\n" "$SOURCE"
      else
         tprintf $"@error{Error!}: Downloading %s not completed\n" "$SOURCE_NAME"
         cleanup 1
      fi
   fi
   RET_SOURCE="$SOURCE"
}

check_md5sum() {
   declare FILE="$1"
   declare MD5SUM="$2"
   tprintf $"@summary{Checksumming %s} - " "${FILE##*/}"
   if [[ "$(md5sum "$FILE" | cut -d ' ' -f 1)" == "$MD5SUM" ]] ; then
      tprintf $"@success{Passed!}\n"
      return 0
   else
      tprintf $"@error{Failed!}\n"
      return 1
   fi
}

fetch_resources() {
   declare RESOURCE_URL="$1"
   fetch_src "$RESOURCE_URL/manifest"
   declare MANIFEST="$RET_SOURCE"
   declare LINE
   while read -r LINE ; do
      declare FILE="${LINE:34}"
      fetch_src "$RESOURCE_URL/$FILE" "$(dirname "$FILE")"
      check_md5sum "$RET_SOURCE" "${LINE:0:32}"
   done < "$MANIFEST"
}

make_snapshot() {
   declare SNAPSHOT="$1"
   declare REPO_TYPE="$2"
   shift 2
   SNAPSHOT="$SOURCES_DIR/$SNAPSHOT"
   if [[ "$(shopt -s nullglob; echo "$SNAPSHOT".*)" ]] ; then
      SNAPSHOT=$(echo "$SNAPSHOT".*)
      if [[ -L "$SNAPSHOT" ]] ; then
         SNAPSHOT="$(readlink "$SNAPSHOT")"
         tprintf $"@summary{Found symlink to snapshot}: %s\n" "$SNAPSHOT"
      else
         tprintf $"@summary{Found snapshot}: %s\n" "$SNAPSHOT"
      fi
   else
      mkdir -p "$SOURCES_DIR"
      make_dir checkout
      declare CHECKOUT="$RET_DIR"
      checkout_$REPO_TYPE "$CHECKOUT" "$@"
      SNAPSHOT="$SNAPSHOT.tar"
      declare COMPRESSION="cat"
      if which xz ; then
         SNAPSHOT="$SNAPSHOT.xz"
         COMPRESSION="xz"
      elif which lzma ; then
         SNAPSHOT="$SNAPSHOT.lzma"
         COMPRESSION="lzma"
      elif which bzip2 ; then
         SNAPSHOT="$SNAPSHOT.bz2"
         COMPRESSION="bzip2"
      elif which gzip ; then
         SNAPSHOT_FILE="$SNAPSHOT.gz"
         COMPRESSION="gzip"
      fi
      tar -c --use-compress-program "$COMPRESSION" -C "$CHECKOUT" -f "$SNAPSHOT" .
      rm -rf "$CHECKOUT"
   fi
   RET_SNAPSHOT="$SNAPSHOT"
}

unpack_archive() {
   declare FILE="$1"
   declare DIR="$2"
   tprintf "@summary{Unpacking archive} %s - " "${FILE##*/}"
   declare UNPACK_DIR="$DIR/src2pkg-ng-$(printf %0X "$(date +%s)")"
   mkdir -p "$UNPACK_DIR"
   mkdir -p "$DIR/metadata"
   IFS=$' \n\t'
   set +e
   case "$FILE" in
      *.rpm)
         if ! rpm_magic "$FILE" ; then
            tprintf $"@error{Failed!}\n"
            echo $"Not an RPM package"
            cleanup 1
         fi
         if (( $(rpm_major "$FILE") != 3 )) ; then
            tprintf $"@error{Failed!}\n"
            echo $"RPM package not version 3"
            cleanup 1
         fi
         declare -a RPM_SIG_INDEX
         rpm_read_index "$FILE" RPM_SIG_INDEX 96
         declare MD5SUM="$(rpm_read_data "$FILE" RPM_SIG_INDEX 1004)"
         declare FILE_MD5SUM="$(dd if="$FILE" bs=1 skip=$(( (RET_END_OFFSET + 7) / 8 * 8 )) 2> /dev/null | md5sum | cut -d ' ' -f 1)"
         if [[ "$FILE_MD5SUM" != "$MD5SUM" ]] ; then
            tprintf $"@error{Failed!}\n"
            echo $"MD5 checksum incorrect"
            cleanup 1
         fi
         declare -a RPM_HEADER_INDEX
         rpm_read_index "$FILE" RPM_HEADER_INDEX $(( (RET_END_OFFSET + 7) / 8 * 8 ))
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1001
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1002
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1004
         #echo "${RET_ARRAY[0]}"
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1005
         #echo "${RET_ARRAY[0]}"
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1014
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1015
         #rpm_read_data "$FILE" RPM_HEADER_INDEX 1020
         dd if="$FILE" bs=1 skip=$RET_END_OFFSET 2> /dev/null | gzip -d | (cd "$UNPACK_DIR" && cpio -idmu --no-absolute-filenames --quiet)
         # FIXME: write out headers into metadata
      ;;
      *.deb)
         if [[ "$(dd if="$FILE" bs=4 count=14 2> /dev/null)" != $'!<arch>\ndebian' ]] ; then
            tprintf $"@error{Failed!}\n"
            echo $"Not a Debian package"
            cleanup 1
         fi
         if [[ "$(ar p "$FILE" "debian-binary")" != "2.0" ]] ; then
            tprintf $"@error{Failed!}\n"
            echo $"Debian package not version 2"
            cleanup 1
         fi
         printf "2.0\n" > "$DIR/metadata/debian-binary"
         mkdir "$DIR/metadata/control"
         ar p "$FILE" control.tar.gz | tar -x -z -C "$DIR/metadata/control"
         if ar t "$FILE" data.tar > /dev/null 2>&1 ; then
            ar p "$FILE" data.tar | tar -x -C "$UNPACK_DIR"
         elif ar t "$FILE" data.tar.gz > /dev/null 2>&1 ; then
            ar p "$FILE" data.tar.gz | tar -x -z -C "$UNPACK_DIR"
         elif ar t "$FILE" data.tar.bz2 > /dev/null 2>&1 ; then
            ar p "$FILE" data.tar.bz2 | tar -x -j -C "$UNPACK_DIR"
         elif ar t "$FILE" data.tar.lzma > /dev/null 2>&1 ; then
            ar p "$FILE" data.tar.lzma | tar -x --use-compress-program lzma -C "$UNPACK_DIR"
         elif ar t "$FILE" data.tar.xz > /dev/null 2>&1 ; then
            ar p "$FILE" data.tar.xz | tar -x --use-compress-program xz -C "$UNPACK_DIR"
         else
            tprintf $"@error{Failed!}\n"
            echo $"Cannot find data tarball in Debian package"
            cleanup 1
         fi
      ;;
      *.tar.gz | *.tgz)
         tar -x -z -C "$UNPACK_DIR" -f "$FILE" > /dev/null
      ;;
      *.tar.bz2 | *.tbz)
         tar -x -j -C "$UNPACK_DIR" -f "$FILE" > /dev/null
      ;;
      *.tar.lzma | *.tlz)
         tar -x --use-compress-program lzma -C "$UNPACK_DIR" -f "$FILE" > /dev/null
      ;;
      *.tar.xz | *.txz)
         tar -x --use-compress-program xz -C "$UNPACK_DIR" -f "$FILE" > /dev/null
      ;;
      *.tar)
         tar -x -C "$UNPACK_DIR" -f "$FILE" > /dev/null
      ;;
      *.zip)
         unzip -d "$UNPACK_DIR" "$FILE" > /dev/null
      ;;
      *)
         tprintf $"@error{Failed!}\n"
         echo $"Unsupported archive type"
         cleanup 1
      ;;
   esac
   declare STATUS=$?
   set -e
   IFS=$'\n'
   if (( STATUS != 0 )) ; then
      tprintf $"@error{Failed!}\n"
      echo $"Archive might be defective"
      cleanup $STATUS
   fi
   shopt -s nullglob dotglob
   if [[ -d "$UNPACK_DIR/install" ]] ; then
      mv "$UNPACK_DIR/install" "$DIR/metadata"
   fi
   declare FILES=("$UNPACK_DIR"/*)
   shopt -u nullglob dotglob
   while (( ${#FILES[@]} == 1 )) && [[ -d "$FILES" ]]; do
      FILES=("$FILES"/*)
   done
   if (( ${#FILES[@]} )) ; then
      mv "${FILES[@]}" "$DIR"
   fi
   rm -rf "$UNPACK_DIR"
   tprintf $"@success{Done}\n"
}

fixup_src() {
   declare SRC_DIR="$1"
   if (( FIX_SRC_PERMS )) ; then
      fixup_src_perms "$SRC_DIR"
   fi
   if (( AUTOPATCH )) ; then
      apply_patches "$SRC_DIR"
   fi
   if (( FIX_AUTOTOOLS )) ; then
      fixup_autotools "$SRC_DIR"
   fi
}

fixup_src_perms() {
   declare SRC_DIR="$1"
   tprintf $"@summary{Correcting source permissions} - "
   chown -R "$OWNER:$GROUP" "$SRC_DIR"
   find "$SRC_DIR" ! -perm 755 -type d -exec chmod 755 "{}" "+"
   find "$SRC_DIR" ! -perm 644 -a ! -perm -100 -a -perm 400 -type f -exec chmod 644 "{}" "+"
   find "$SRC_DIR" ! -perm 755 -a -perm -500 -type f -exec chmod 755 "{}" "+"
   tprintf $"@success{Done}\n"
}

apply_patches() {
   declare SRC_DIR="$1"
   declare -a PATCHES
   if (( ${#PATCHES[@]} )) ; then
      tprintf $"@summary{Applying user-specified patches}\n"
      declare PATCH
      for PATCH in "${PATCHES[@]}" ; do
         apply_patch "$PATCH" "$SRC_DIR"
      done
   else
      shopt -s nullglob
      push_back PATCHES "$PATCH_DIR"/*.patch* "$PATCH_DIR"/*.diff* "$PATCH_DIR"/*.dpatch
      shopt -u nullglob
      if (( ${#PATCHES[@]} )) ; then
         tprintf $"@summary{Applying patches found in} %s\n" "$PATCH_DIR"
         declare PATCH
         for PATCH in "${PATCHES[@]}" ; do
            apply_patch "$PATCH" "$SRC_DIR"
         done
      else
         tprintf $"@summary{No patches found in} %s\n" "$PATCH_DIR"
      fi
   fi
}

apply_patch() {
   declare PATCH="$1"
   declare DIR="$2"
   if [[ "${PATCH:0:1}" != "/" ]] ; then
      PATCH="$PATCH_DIR/$PATCH"
   fi
   tprintf "@summary{Applying} %s\n" "${PATCH##*/}"
   declare LEVEL
   for LEVEL in 1 0 2 3 4 5 ; do
      IFS=$' \n\t'
      set +e
      case "$PATCH" in
         *.diff | *.patch)
            patch -l -d "$DIR" -p$LEVEL < "$PATCH"
         ;;
         *.dpatch)
            declare HEADER=1
            while read LINE && [[ "$LINE" != "@DPATCH@" ]] ; do
               (( ++HEADER ))
            done < "$PATCH"
            tail -n +$HEADER "$PATCH" | patch -l -d "$DIR"
         ;;
         *.gz)
            gzip -c "$PATCH" | patch -l -d "$DIR" -p$LEVEL
         ;;
         *.bz2)
            bzip2 -c "$PATCH" |patch -l -d "$DIR" -p$LEVEL
         ;;
         *.xz | *.lzma)
            xz -c "$PATCH" | patch -l -d "$DIR" -p$LEVEL
         ;;
         *)
            tprintf $"@error{Failed!}\n"
            echo $"Unsupported patch type"
            return 1
         ;;
      esac > "$DIR/current-patchlog"
      declare STATUS=$?
      set -e
      IFS=$'\n'
      if (( STATUS == 0 )) ; then
         cut -f 3 -d ' ' "$DIR/current-patchlog" | sort | uniq >> "$DIR/$NAME-patched-files.log"
         rm -f "$DIR/current-patchlog"
         break
      fi
   done
   rm -f "$DIR/current-patchlog"
}

fixup_autotools() {
   declare SRC_DIR="$1"
   #find "$SRC_DIR" -type f -name "config.guess" -exec cp -f "$SRC2PKG_NG_DIR/autotools_files/config.guess" "{}" ";"
   #find "$SRC_DIR" -type f -name "config.sub" -exec cp -f "$SRC2PKG_NG_DIR/autotools_files/config.sub" "{}" ";"
   find "$SRC_DIR" -type f -name "config.guess" -exec cp -f "/$DATA_DIR/libtool/config.guess" "{}" ";"
   find "$SRC_DIR" -type f -name "config.sub" -exec cp -f "/$DATA_DIR/libtool/config.sub" "{}" ";"
   find "$SRC_DIR" -type f -name "install-sh" -exec cp -f "/$DATA_DIR/libtool/install-sh" "{}" ";"
   #find "$SRC_DIR" -type f -name "ltmain.sh" -exec cp -f "/$DATA_DIR/libtool/ltmain.sh" "{}" ";"
   declare INPUT
   for INPUT in $(find "$SRC_DIR" -type f -name "configure.in" -o -name "configure.ac") ; do
      declare OUTPUT="${INPUT%.*}"
      declare REGEN_DIR="$(dirname "$INPUT")"
      declare DO_REGEN=0
      if [[ ! -e "$OUTPUT" ]] ; then
         DO_REGEN=1
      else
      #elif ! grep -q "^$INPUT\$" "$SRC_DIR/$NAME-patched-files.log" ; then
         declare FILE
         for FILE in "$INPUT" $(grep "m4_include" "$INPUT" | sed "s/.*\[\(.*\)\].*/\1/") ; do
            if (cd "$REGEN_DIR" && [[ "$FILE" -nt "$OUTPUT" ]]) ; then
            #if grep -q "^$FILE\$" "$SRC_DIR/$NAME-patched-files.log" ; then
               DO_REGEN=1
               break
            fi
         done
      fi
      if (( DO_REGEN )) ; then
         tprintf $"@notice{Notice} - configure script missing or stale in %s\n" "$REGEN_DIR"
         tprintf $"@summary{Regenerating config files} - "
         if [[ -e "$REGEN_DIR/autogen.sh" ]] ; then
            (cd "$REGEN_DIR" && sh autogen.sh) > /dev/null 2>&1
         else
            (cd "$REGEN_DIR" && autoreconf -i -f) > /dev/null 2>&1
            (cd "$REGEN_DIR" && autoconf) > /dev/null 2>&1
         fi
         tprintf $"@success{Done}\n"
      fi
   done
}
