#!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/>.

read_char() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   declare LENGTH="$3"
   dd if="$FILE" bs=1 count=$LENGTH skip=$OFFSET 2> /dev/null
}

read_int8() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   dd if="$FILE" bs=1 count=1 skip=$OFFSET 2> /dev/null | od -A n -t u1
}

read_int16() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   echo $(( $(read_int8 "$FILE" $OFFSET) * 256 + $(read_int8 "$FILE" $(( OFFSET + 1))) ))
}

read_int32() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   echo $(( $(read_int16 "$FILE" $OFFSET) * 256 * 256 + $(read_int16 "$FILE" $(( OFFSET + 2))) ))
}

read_int64() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   echo $(( $(read_int32 "$FILE" $OFFSET) * 256 * 256 * 256 * 256 + $(read_int32 "$FILE" $(( OFFSET + 4))) ))
}

read_string() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   declare BIN="$(dd if="$FILE" bs=1 count=64 skip=$OFFSET 2> /dev/null | od -A n -t x1 | tr -d \$'\n *')"
   (( OFFSET += 64 ))
   declare STR=""
   while [[ "${BIN:0:2}" != "00" ]] ; do
      STR="$STR$(printf"\x${BIN:0:2}")"
      BIN="${BIN:2}"
      if [[ ! "$BIN" ]] ; then
         BIN="$(dd if="$FILE" bs=1 count=64 skip=$OFFSET 2> /dev/null | od -A n -t x1 | tr -d \$'\n *')"
         (( OFFSET += 64 ))
      fi
   done
   echo "$STR"
}

read_bin() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   declare LENGTH="$3"
   dd if="$FILE" bs=1 count=$LENGTH skip=$OFFSET 2> /dev/null | od -A n -t x1 | tr -d \$'\n *'
}

read_string_array() {
   set -eu
   declare FILE="$1"
   declare OFFSET="$2"
   declare COUNT="$3"
   declare ARRAY_VAR="$4"
   unset $ARRAY_VAR
   declare BIN="$(dd if="$FILE" bs=1 count=64 skip=$OFFSET 2> /dev/null | od -A n -t x1 | tr -d \$'\n *')"
   (( OFFSET += 64 ))
   while (( COUNT > 0 )) ; do
      declare STR=""
      while [[ "${BIN:0:2}" != "00" ]] ; do
         STR="$STR$(printf "\x${BIN:0:2}")"
         BIN="${BIN:2}"
         if [[ ! "$BIN" ]] ; then
            BIN="$(dd if="$FILE" bs=1 count=64 skip=$OFFSET 2> /dev/null | od -A n -t x1 | tr -d \$'\n *')"
            (( OFFSET += 64 ))
         fi
      done
      push_back "$ARRAY_VAR" "$STR"
      (( --COUNT ))
   done
}

write_nulls() {
   declare FILE="$1"
   declare OFFSET="$2"
   declare LENGTH="$3"
   dd if=/dev/zero of="$FILE" bs=1 seek=$OFFSET count=$LENGTH 2> /dev/null
}

rpm_magic() {
   [[ "$(dd if="$1" bs=1 count=4 2> /dev/null)" == $'\xED\xAB\xEE\xDB' ]]
}

rpm_major() {
   set -eu
   read_int8 "$1" 4
}

rpm_minor() {
   set -eu
   read_int8 "$1" 5
}

rpm_type() {
   set -eu
   read_int16 "$1" 6
}

rpm_archnum() {
   set -eu
   read_int16 "$1" 8
}

rpm_name() {
   set -eu
   dd if="$1" bs=1 count=66 skip=10 2> /dev/null
}

rpm_osnum() {
   set -eu
   read_int16 "$1" 76
}

rpm_sig_type() {
   set -eu
   read_int16 "$1" 78
}

rpm_read_index() {
   declare FILE="$1"
   declare INDEX_VAR="$2"
   declare OFFSET="$3"
   if [[ "$(dd if="$FILE" bs=1 count=3 skip=$OFFSET 2> /dev/null)" != $'\x8E\xAD\xE8' ]] ; then
      tprintf $"@error{Error!}: RPM header not valid\n"
      return 1
   fi
   declare HEADER_VERSION="$(read_int8 "$FILE" $(( OFFSET + 3 )))"
   declare INDEX_ENTRIES="$(read_int32 "$FILE" $(( OFFSET + 8 )))"
   declare STORE_SIZE="$(read_int32 "$FILE" $(( OFFSET + 12 )))"
   declare INDEX=0
   while (( INDEX != INDEX_ENTRIES )) ; do
      declare INDEX_OFFSET=$(( OFFSET + 16 + INDEX * 16 ))
      declare TAG=$(read_int32 "$FILE" $INDEX_OFFSET)
      declare TYPE=$(read_int32 "$FILE" $(( INDEX_OFFSET + 4 )))
      declare DATA_OFFSET=$(read_int32 "$FILE" $(( INDEX_OFFSET + 8 )))
      declare DATA_COUNT=$(read_int32 "$FILE" $(( INDEX_OFFSET + 12 )))
      declare FILE_OFFSET=$(( OFFSET + 16 + INDEX_ENTRIES * 16 + DATA_OFFSET ))
      eval "$INDEX_VAR[$TAG]=\"$TYPE $FILE_OFFSET $DATA_COUNT\""
      (( ++INDEX ))
   done
   RET_END_OFFSET=$(( OFFSET + 16 + INDEX_ENTRIES * 16 + STORE_SIZE ))
}

rpm_read_data() {
   set -eu
   declare FILE="$1"
   declare INDEX_VAR="$2"
   declare TAG="$3"
   eval "declare INDEX_ENTRY=\"\${$INDEX_VAR[$TAG]}\""
   declare TYPE=$(echo "$INDEX_ENTRY" | cut -d " " -f 1)
   declare OFFSET=$(echo "$INDEX_ENTRY" | cut -d " " -f 2)
   declare COUNT=$(echo "$INDEX_ENTRY" | cut -d " " -f 3)
   case $TYPE in
      0)
      ;;
      1)
         read_char "$FILE" $OFFSET $COUNT
      ;;
      2)
         read_int8 "$FILE" $OFFSET
      ;;
      3)
         read_int16 "$FILE" $OFFSET
      ;;
      4)
         read_int32 "$FILE" $OFFSET
      ;;
      5)
         read_int64 "$FILE" $OFFSET
      ;;
      6)
         read_string "$FILE" $OFFSET
      ;;
      7)
         read_bin "$FILE" $OFFSET $COUNT
      ;;
      8)
         read_string_array "$FILE" $OFFSET $COUNT RET_ARRAY
      ;;
      9)
         read_string_array "$FILE" $OFFSET $COUNT RET_ARRAY
      ;;
      *)
         tprintf $"@warning{Warning!}: Header data type %s unsupported\n" "$TYPE"
      ;;
   esac
}
