
############################################################### smallutils

smallyes() {
  YES="${1-y}"
  while echo "$YES" ; do : ; done
}

############################################################### interaction

error () {
  # <error code> <name> <string> <args>
  local err="$1"
  local name="$2"
  local fmt="$3"
  shift; shift; shift
  if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
    (echo "E: $name"
    for x in "$@"; do echo "EA: $x"; done
    echo "EF: $fmt") >&4
  elif [ "$USE_GETTEXT_INTERACTION" ]; then
    (printf "E: `LANG=$GETTEXT_LANG gettext debootstrap "$fmt"`\n" "$@") >&4
  else
    (printf "E: $fmt\n" "$@") >&4
  fi
  exit $err
}

warning () {
  # <name> <string> <args>
  local name="$1"
  local fmt="$2"
  shift; shift
  if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
    (echo "W: $name"
    for x in "$@"; do echo "WA: $x"; done
    echo "WF: $fmt") >&4
  elif [ "$USE_GETTEXT_INTERACTION" ]; then
    printf "W: `LANG=$GETTEXT_LANG gettext debootstrap "$fmt"`\n" "$@" >&4
  else
    printf "W: $fmt\n" "$@" >&4
  fi
}

info () {
  # <name> <string> <args>
  local name="$1"
  local fmt="$2"
  shift; shift
  if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
    (echo "I: $name"
    for x in "$@"; do echo "IA: $x"; done
    echo "IF: $fmt") >&4
  elif [ "$USE_GETTEXT_INTERACTION" ]; then
    printf "I: `LANG=$GETTEXT_LANG gettext debootstrap "$fmt"`\n" "$@" >&4
  else
    printf "I: $fmt\n" "$@" >&4
  fi
}

PROGRESS_NOW=0
PROGRESS_END=0
PROGRESS_NEXT=""
PROGRESS_WHAT=""

progress_next () {
  PROGRESS_NEXT="$1"
}

wgetprogress () {
  [ ! "$verbose" ] && QSWITCH="-q"
  local ret=0
  if [ "$USE_DEBIANINSTALLER_INTERACTION" -a "$PROGRESS_NEXT" ]; then
    wget "$@" 2>&1 >/dev/null | $PKGDETAILS "WGET%" $PROGRESS_NOW $PROGRESS_NEXT $PROGRESS_END >&3
    ret=$?
  elif [ "$USE_BOOTFLOPPIES_INTERACTION" -a "$PROGRESS_NEXT" ]; then
    wget "$@" 2>&1 >/dev/null | $PKGDETAILS "WGET%" $PROGRESS_NOW $PROGRESS_NEXT $PROGRESS_END "$PROGRESS_WHAT" >&3
    ret=$?
  else
    wget $QSWITCH "$@"
    ret=$?
  fi
  return $ret
}

progress () {
  # <now> <end> <name> <string> <args>
  local now="$1"
  local end="$2"
  local name="$3"
  local fmt="$4"
  shift; shift; shift; shift
  if [ "$USE_DEBIANINSTALLER_INTERACTION" ]; then
    PROGRESS_NOW="$now"
    PROGRESS_END="$end"
    PROGRESS_NEXT=""
    (echo "P: $now $end $name"
    for x in "$@"; do echo "PA: $x"; done
    echo "PF: $fmt") >&3
  elif [ "$USE_BOOTFLOPPIES_INTERACTION" ]; then
    PROGRESS_NOW="$now"
    PROGRESS_END="$end"
    PROGRESS_WHAT="`printf "$fmt" "$@"`"
    PROGRESS_NEXT=""
    printf "P: %s %s %s\n" $now $end "$PROGRESS_WHAT" >&3
  fi
}

dpkg_progress () {
  # <now> <end> <name> <desc> UNPACKING|CONFIGURING
  local now="$1"
  local end="$2"
  local name="$3"
  local desc="$4"
  local action="$5"
  local expect=

  if [ "$action" = UNPACKING ]; then
    expect=half-installed
  elif [ "$action" = CONFIGURING ]; then
    expect=half-configured
  fi

  dp () {
    now="$(($now + ${1:-1}))"
  }

  exitcode=0
  while read status pkg qstate; do
    if [ "$status" = "EXITCODE" ]; then
      exitcode="$pkg"
      continue
    fi
    [ "$qstate" = "$expect" ] || continue
    case $qstate in
      half-installed)
        dp; progress "$now" "$end" "$name" "$desc"
        info "$action" "Unpacking %s..." "${pkg%:}"
        expect=unpacked
        ;;
      unpacked)
        expect=half-installed
        ;;
      half-configured)
        dp; progress "$now" "$end" "$name" "$desc"
        info "$action" "Configuring %s..." "${pkg%:}"
        expect=installed
        ;;
      installed)
        expect=half-configured
        ;;
    esac
  done
  return $exitcode
}

############################################################# set variables

default_mirror () {
  DEF_MIRROR="$1"
}

FINDDEBS_NEEDS_INDICES=false
finddebs_style () {
  case "$1" in
    "hardcoded")
      ;;
    "from-indices")
      FINDDEBS_NEEDS_INDICES=true
      ;;
    *)
      error 1 BADFINDDEBS "unknown finddebs style"
      ;;
   esac
}

mk_download_dirs () {
  if [ $DLDEST = "apt_dest" ]; then
    mkdir -p "$TARGET/$APTSTATE/lists/partial"
    mkdir -p "$TARGET/var/cache/apt/archives/partial"
  fi
}

download_style () {
  case "$1" in
    "apt")
      if [ "$2" = "var-state" ]; then
	APTSTATE=var/state/apt
      else
        APTSTATE=var/lib/apt
      fi
      DLDEST=apt_dest
      export APTSTATE DLDEST DEBFOR
      ;;
    *)
      error 1 BADDLOAD "unknown download style"
      ;;
  esac
}

########################################################## variant handling

doing_variant () {
  if [ "$1" = "$VARIANT" ]; then return 0; fi
  if [ "$1" = "-" -a "$VARIANT" = "" ]; then return 0; fi
  return 1
}

SUPPORTED_VARIANTS="-"
variants () {
  SUPPORTED_VARIANTS="$*"
  for v in $*; do
    if doing_variant "$v"; then return 0; fi
  done
  error 1 UNSUPPVARIANT "unsupported variant"
}

################################################# work out names for things

mirror_style () {
  case "$1" in
    "release")
      DOWNLOAD_INDICES=download_release_indices
      DOWNLOAD_DEBS=download_release
      ;;
    "main")
      DOWNLOAD_INDICES=download_main_indices
      DOWNLOAD_DEBS=download_main
      ;;
    *)
      error 1 BADMIRROR "unknown mirror style"
      ;;
  esac
  export DOWNLOAD_INDICES
  export DOWNLOAD_DEBS
}

check_md5 () {
  # args: dest md5 size
  local expmd5="$2"
  local expsize="$3"
  relmd5=`md5sum < "$1" | sed 's/ .*$//'`
  relsize=`wc -c < "$1"`
  if [ "$expsize" -ne "$relsize" -o "$expmd5" != "$relmd5" ]; then
    return 1
  fi
  return 0
}

get () {
  # args: from dest [md5sum size] [alt {md5sum size type}]
  local displayname
  if [ "${2%.deb}" != "$2" ]; then
    displayname="$(echo "$2" | sed 's,^.*/,,;s,_.*$,,')"
  else
    displayname="$(echo "$1" | sed 's,^.*/,,')"
  fi
  
  if [ -e "$2" ]; then
    if [ "$3" = "" ]; then
      return 0
    fi
    info VALIDATING "Validating %s" "$displayname"
    if check_md5 "$2" "$3" "$4"; then
      return 0
    else
      rm -f "$2"
    fi
  fi
  if [ "$#" -gt 5 ]; then
    local st=3
    if [ "$5" = "-" ]; then st=6; fi
    local order="$(a=$st; while [ "$a" -le $# ]; do eval echo \"\${$(($a+1))}\" $a;
                  a=$(($a + 3)); done | sort -n | sed 's/.* //')"
  else
    local order=3
  fi
  for a in $order; do
    local md5="$(eval echo \${$a})"
    local siz="$(eval echo \${$(( $a+1 ))})"
    local typ="$(eval echo \${$(( $a+2 ))})"
    local from
    local dest

    case "$typ" in
      "bz2") from="$1.bz2"; dest="$2.bz2" ;;
      "gz") from="$1.gz"; dest="$2.gz" ;;
      *) from="$1"; dest="$2" ;;
    esac

    if [ "${dest#/}" = "$dest" ]; then
      dest="./$dest"
    fi
    local dest2="$dest"
    if [ -d "${dest2%/*}/partial" ]; then
      dest2="${dest2%/*}/partial/${dest2##*/}"
    fi

    info RETRIEVING "Retrieving %s" "$displayname"
    if ! just_get "$from" "$dest2"; then continue; fi
    if [ "$md5" != "" ]; then
      info VALIDATING "Validating %s" "$displayname"
      if check_md5 "$dest2" "$md5" "$siz"; then
        md5=""
      fi
    fi
    if [ "$md5" = "" ]; then
      [ "$dest2" = "$dest" ] || mv "$dest2" "$dest"
      case "$typ" in
        "gz") gunzip "$dest" ;;
        "bz2") bunzip2 "$dest" ;;
      esac
      return 0
    else
      warning CORRUPTFILE "%s was corrupt" "$from"
    fi
  done
  return 1
}

just_get () {
  # args: from dest
  local from="$1"
  local dest="$2"
  mkdir -p "${dest%/*}"
  if [ "${from#null:}" != "$from" ]; then
    error 1 NOTPREDL "%s was not pre-downloaded" "${from#null:}"
  elif [ "${from#http://}" != "$from" -o "${from#ftp://}" != "$from" ]; then
    # http/ftp mirror
    if wgetprogress -O "$dest" "$from"; then
      return 0
    elif [ -s "$dest" ]; then
      local iters=0
      while [ "$iters" -lt 3 ]; do
        warning RETRYING "Retrying failed download of %s" "$from"
        if wgetprogress -c -O "$dest" "$from"; then break; fi
        iters="$(($iters + 1))"
      done
    else
      rm -f "$dest"
      return 1
    fi
  elif [ "${from#file:}" != "$from" ]; then
    local base="${from#file:}"
    if [ "${base#//}" != "$base" ]; then
      base="/${from#file://*/}"
    fi
    if [ -e "$base" ]; then
      cp "$base" "$dest"
      return 0
    else
      return 1
    fi
  else
    error 1 UNKNOWNLOC "unknown location %s" "$from"
  fi
}

download () {
  mk_download_dirs
  "$DOWNLOAD_DEBS" $(echo "$@" | tr ' ' '\n' | sort)
}

download_indices () {
  mk_download_dirs
  "$DOWNLOAD_INDICES" $(echo "$@" | tr ' ' '\n' | sort)
}

debfor () {
    (while read pkg path; do
      for p in "$@"; do
        [ "$p" = "$pkg" ] || continue;
        echo "$path"
      done
    done <"$TARGET/debootstrap/debpaths"
  )
}

apt_dest () {
  # args:
  #   deb package version arch mirror path
  #   pkg suite component arch mirror path
  #   rel suite mirror path
  case "$1" in
    "deb")
      echo "var/cache/apt/archives/${2}_${3}_${4}.deb" | sed 's/:/%3a/'
      ;;
    "pkg")
      local m="$5"
      m="debootstrap.invalid"
      #if [ "${m#http://}" != "$m" ]; then
      #  m="${m#http://}"
      #elif [ "${m#file://}" != "$m" ]; then
      #  m="file_localhost_${m#file://*/}"
      #elif [ "${m#file:/}" != "$m" ]; then
      #  m="file_localhost_${m#file:/}"
      #fi

      printf "%s" "$APTSTATE/lists/"
      echo "${m}_$6" | sed 's/\//_/g'
      ;;
    "rel")
      local m="$3"
      m="debootstrap.invalid"
      #if [ "${m#http://}" != "$m" ]; then
      #  m="${m#http://}"
      #elif [ "${m#file://}" != "$m" ]; then
      #  m="file_localhost_${m#file://*/}"
      #elif [ "${m#file:/}" != "$m" ]; then
      #  m="file_localhost_${m#file:/}"
      #fi
      printf "%s" "$APTSTATE/lists/"
      echo "${m}_$4" | sed 's/\//_/g'
      ;;
  esac
}

################################################################## download

get_release_md5 () {
  local reldest="$1"
  local path="$2"
  sed -n '/^[Mm][Dd]5[Ss][Uu][Mm]/,/^[^ ]/p' < "$reldest" | while read a b c; do
    if [ "$c" = "$path" ]; then echo "$a $b"; fi
  done | head -n 1
}

download_release_sig () {
  local m1="$1"
  local reldest="$2"
  local relsigdest="$TARGET/$($DLDEST rel "$SUITE" "$m1" "dists/$SUITE/Release.gpg")"

  if [ -n "$KEYRING" ]; then
    progress 0 100 DOWNRELSIG "Downloading Release file signature"
    progress_next 50
    get "$m1/dists/$SUITE/Release.gpg" "$relsigdest" ||
      error 1 NOGETRELSIG "Failed getting release signature file %s" \
        "$m1/dists/$SUITE/Release.gpg"
    progress 50 100 DOWNRELSIG "Downloading Release file signature"

    info RELEASESIG "Checking Release signature"
    # Don't worry about the exit status from gpgv; parsing the output will
    # take care of that.
    (gpgv --status-fd 1 --keyring "$KEYRING" --ignore-time-conflict \
     "$relsigdest" "$reldest" || true) | read_gpg_status
    progress 100 100 DOWNRELSIG "Downloading Release file signature"
  fi
}

download_release_indices () {
  local m1="${MIRRORS%% *}"
  local reldest="$TARGET/$($DLDEST rel "$SUITE" "$m1" "dists/$SUITE/Release")"
  progress 0 100 DOWNREL "Downloading Release file"
  progress_next 100
  get "$m1/dists/$SUITE/Release" "$reldest" ||
    error 1 NOGETREL "Failed getting release file %s" "$m1/dists/$SUITE/Release"

  TMPCOMPONENTS="$(sed -n 's/Components: *//p' "$reldest")"
  for c in $TMPCOMPONENTS ; do
      eval "
    case \"\$c\" in
      $USE_COMPONENTS)
        COMPONENTS=\"\$COMPONENTS \$c\"
        ;;
    esac
           "
  done
  COMPONENTS="$(echo $COMPONENTS)"

  if [ "$COMPONENTS" = "" ]; then
    mv "$reldest" "$reldest.malformed"
    error 1 INVALIDREL "Invalid Release file, no valid components"
  fi
  progress 100 100 DOWNREL "Downloading Release file"

  download_release_sig "$m1" "$reldest"

  local totalpkgs=0
  for c in $COMPONENTS; do
      local subpath="$c/binary-$ARCH/Packages"
      local normmd="`get_release_md5 "$reldest" "$subpath"`"
      if [ "$normmd" = "" ]; then
        mv "$reldest" "$reldest.malformed"
        error 1 MISSINGRELENTRY "Invalid Release file, no entry for %s" "$subpath"
      fi
      totalpkgs="$(( $totalpkgs + ${normmd#* } ))"
  done

  local donepkgs=0
  progress 0 $totalpkgs DOWNPKGS "Downloading Packages files"
  for c in $COMPONENTS; do
    local subpath="$c/binary-$ARCH/Packages"
    local path="dists/$SUITE/$subpath"
    local bz2md="`get_release_md5 "$reldest" "$subpath.bz2"`"
    local gzmd="`get_release_md5 "$reldest" "$subpath.gz"`"
    local normmd="`get_release_md5 "$reldest" "$subpath"`"
    local ext="$normmd ."
    if [ -x /bin/bunzip2 -a "$bz2md" != "" ]; then
      ext="$ext $bz2md bz2"
    fi
    if [ -x /bin/gunzip -a "$gzmd" != "" ]; then
      ext="$ext $gzmd gz"
    fi
    progress_next "$(($donepkgs + ${normmd#* }))"
    for m in $MIRRORS; do
      local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"
      if get "$m/$path" "$pkgdest" $ext; then break; fi
    done
    donepkgs="$(($donepkgs + ${normmd#* }))"
    progress $donepkgs $totalpkgs DOWNPKGS "Downloading Packages files"
  done
}

get_package_sizes () {
  # mirror pkgdest debs..
  local m="$1"; shift
  local pkgdest="$1"; shift
  $PKGDETAILS PKGS "$m" "$pkgdest" "$@" | (
    newleft=""
    totaldebs=0
    countdebs=0
    while read p details; do
      if [ "$details" = "-" ]; then 
	newleft="$newleft $p"
      else
        size="${details##* }";
        totaldebs="$(($totaldebs + $size))"
        countdebs="$(($countdebs + 1))"
      fi
    done
    echo "$countdebs $totaldebs$newleft"
  )
}

# note, leftovers come back on fd5 !!
download_debs () {
  local m="$1"
  local pkgdest="$2"
  shift; shift

  $PKGDETAILS PKGS "$m" "$pkgdest" "$@" | (
    leftover=""
    while read p ver arc mdup fil md5 size; do
      if [ "$ver" = "-" ]; then
        leftover="$leftover $p"
      else 
        progress_next "$(($dloaddebs + $size))"
        local debdest="$($DLDEST deb "$p" "$ver" "$arc" "$m" "$fil")"
        if get "$m/$fil" "$TARGET/$debdest" "$md5" "$size"; then
          dloaddebs="$(($dloaddebs + $size))"
          echo >>$TARGET/debootstrap/debpaths "$p $debdest"
        else
          warning COULDNTDL "Couldn't download package %s" "$p"
        fi
      fi
    done
    echo >&5 ${leftover# }
  )
}

download_release () {
  local m1="${MIRRORS%% *}"

  local numdebs="$#"

  local countdebs=0
  progress $countdebs $numdebs SIZEDEBS "Finding package sizes"
  
  local totaldebs=0
  local leftoverdebs="$*"
  for c in $COMPONENTS; do
    if [ "$countdebs" -ge "$numdebs" ]; then break; fi

    local path="dists/$SUITE/$c/binary-$ARCH/Packages"
    local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")"
    if [ ! -e "$pkgdest" ]; then continue; fi

    info CHECKINGSIZES "Checking component %s on %s..." "$c" "$m1"

    leftoverdebs="$(get_package_sizes "$m1" "$pkgdest" $leftoverdebs)"

    countdebs=$(($countdebs + ${leftoverdebs%% *}))
    leftoverdebs=${leftoverdebs#* }

    totaldebs=${leftoverdebs%% *}
    leftoverdebs=${leftoverdebs#* }

    progress $countdebs $numdebs SIZEDEBS "Finding package sizes"
  done 

  if [ "$countdebs" -ne "$numdebs" ]; then
    error 1 LEFTOVERDEBS "Couldn't find these debs: %s" "$leftoverdebs"
  fi

  local dloaddebs=0

  progress $dloaddebs $totaldebs DOWNDEBS "Downloading packages"
  :>$TARGET/debootstrap/debpaths

  pkgs_to_get="$*"
  for c in $COMPONENTS; do
    local path="dists/$SUITE/$c/binary-$ARCH/Packages"
    for m in $MIRRORS; do
      local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"
      if [ ! -e "$pkgdest" ]; then continue; fi
      pkgs_to_get="$(download_debs "$m" "$pkgdest" $pkgs_to_get 5>&1 1>&6)"
      if [ "$pkgs_to_get" = "" ]; then break; fi
    done 6>&1
    if [ "$pkgs_to_get" = "" ]; then break; fi
  done
  progress $dloaddebs $totaldebs DOWNDEBS "Downloading packages"
  if [ "$pkgs_to_get" != "" ]; then
    error 1 COULDNTDLPKGS "Couldn't download packages: %s" "$pkgs_to_get"
  fi
}

download_main_indices () {
  local m1="${MIRRORS%% *}"
  progress 0 100 DOWNMAINPKGS "Downloading Packages file"
  progress_next 100
  COMPONENTS=main
  export COMPONENTS
  for m in $MIRRORS; do
    for c in $COMPONENTS; do
      local path="dists/$SUITE/$c/binary-$ARCH/Packages"
      local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"
      if [ -x /bin/gunzip ] && get "$m/${path}.gz" "${pkgdest}.gz"; then
        rm -f "$pkgdest"
        gunzip "$pkgdest.gz"
      elif get "$m/$path" "$pkgdest"; then
        true
      fi
    done
  done
  progress 100 100 DOWNMAINPKGS "Downloading Packages file"
}

download_main () {
  local m1="${MIRRORS%% *}"

  :>$TARGET/debootstrap/debpaths
  for p in "$@"; do
    for c in $COMPONENTS; do
      local details=""
      for m in $MIRRORS; do
        local path="dists/$SUITE/$c/binary-$ARCH/Packages"
        local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"
        if [ ! -e "$pkgdest" ]; then continue; fi
        details="$($PKGDETAILS PKGS "$m" "$pkgdest" "$p")"
        if [ "$details" = "$p -" ]; then continue; fi
        size="${details##* }"; details="${details% *}"
        md5="${details##* }"; details="${details% *}"
        local debdest="$($DLDEST deb $details)"
        if get "$m/${details##* }" "$TARGET/$debdest" "$md5" "$size"; then
	  echo >>$TARGET/debootstrap/debpaths "$p $debdest"
          details="done"
          break
        fi
      done
      if [ "$details" != "" ]; then
        break
      fi
    done
    if [ "$details" != "done" ]; then
      error 1 COULDNTDL "Couldn't download %s" "$p"
    fi
  done
}

###################################################### deb choosing support

get_debs () {
  local field="$1"
  shift
  local m1 c
  for m1 in $MIRRORS; do
    for c in $COMPONENTS; do
      local path="dists/$SUITE/$c/binary-$ARCH/Packages"
      local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")"
      echo $("$PKGDETAILS" FIELD "$field" "$m1" "$pkgdest" "$@" | sed 's/ .*//')
    done
  done
}

################################################################ extraction

extract () { (
  cd "$TARGET"
  local p=0
  for pkg in $(debfor "$@"); do
    p="$(($p + 1))"
    progress "$p" "$#" EXTRACTPKGS "Extracting packages"
    packagename="$(echo "$pkg" | sed 's,^.*/,,;s,_.*$,,')"
    info EXTRACTING "Extracting %s..." "$packagename"
    ar -p "./$pkg" data.tar.gz | zcat | tar -xf -
  done
); }

in_target_nofail () {
  if ! chroot "$TARGET" "$@" 2>/dev/null; then
    true
  fi
  return 0
}

in_target_failmsg () {
  local code="$1"
  local msg="$2"
  local arg="$3"
  shift; shift; shift
  if ! chroot "$TARGET" "$@"; then
    warning "$code" "$msg" "$arg"
    return 1
  fi
  return 0
}

in_target () {
  in_target_failmsg IN_TARGET_FAIL "Failure trying to run: %s" "chroot $TARGET $*" "$@"
}

###################################################### standard setup stuff

conditional_cp () {
  if [ ! -e "$2/$1" ]; then
    if [ -L "$1" ]; then
      cat "$1" >"$2/$1"
    elif [ -e "$1" ]; then
      cp -a "$1" "$2/$1"
    fi
  fi
}

mv_invalid_to () {
  local m="$1"
  m="$(echo "${m#http://}" | tr '/' '_' | sed 's/_*//')"
  (cd "$TARGET/$APTSTATE/lists"
   for a in debootstrap.invalid_*; do
     mv "$a" "${m}_${a#*_}"
   done
  )
}

setup_apt_sources () {
  mkdir -p "$TARGET/etc/apt"
  for m in "$@"; do
    local cs=""
    for c in $COMPONENTS; do
      local path="dists/$SUITE/$c/binary-$ARCH/Packages"
      local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m" "$path")"
      if [ -e "$pkgdest" ]; then cs="$cs $c"; fi
    done
    if [ "$cs" != "" ]; then echo "deb $m $SUITE$cs"; fi
  done > "$TARGET/etc/apt/sources.list"
}

setup_etc () {
  mkdir -p "$TARGET/etc"
  
  conditional_cp /etc/resolv.conf "$TARGET"
  conditional_cp /etc/hostname "$TARGET"
  
  if [ "$DLDEST" = apt_dest -a ! -e "$TARGET/etc/apt/sources.list" ]; then
    setup_apt_sources "http://debootstrap.invalid/"
  fi
}

UMOUNT_DIRS=

umount_exit_function () {
  for dir in $UMOUNT_DIRS; do
    umount "$TARGET/${dir#/}" || true
  done
}

umount_on_exit () {
  if [ "$UMOUNT_DIRS" ]; then
    UMOUNT_DIRS="$UMOUNT_DIRS $1"
  else
    UMOUNT_DIRS="$1"
    on_exit umount_exit_function
  fi
}

setup_proc () {
  case "$ARCH" in
    kfreebsd-*)
      umount_on_exit /dev
      umount_on_exit /proc
      umount "$TARGET/proc" 2>/dev/null || true
      in_target mount -t linprocfs proc /proc
    ;;
    hurd-*)
    ;;
    *)
      umount_on_exit /dev/pts
      umount_on_exit /dev/shm
      umount_on_exit /proc/bus/usb
      umount_on_exit /proc
      umount "$TARGET/proc" 2>/dev/null || true
      in_target mount -t proc proc /proc
      if [ -d "$TARGET/sys" ] && grep -q '[[:space:]]sysfs' /proc/filesystems 2>/dev/null; then
        umount_on_exit /sys
        umount "$TARGET/sys" 2>/dev/null || true
        in_target mount -t sysfs sysfs /sys
      fi
    ;;
  esac
}

setup_proc_fakechroot () {
  rm -rf "$TARGET/proc"
  ln -s /proc "$TARGET"
}

setup_devices () {
  case "$ARCH" in
    kfreebsd-*)
      in_target mount -t devfs devfs /dev ;;
    hurd-*)
      setup_devices_hurd ;;
    *)
      if [ -e "$DEVICES_TARGZ" ]; then
        (cd "$TARGET"; zcat "$DEVICES_TARGZ" | tar -xf -)
      else
        if [ -e /dev/.devfsd ] ; then
          in_target mount -t devfs devfs /dev
        else
          error 1 NODEVTGZ "no %s. cannot create devices" "$DEVICES_TARGZ"
        fi
      fi
    ;;
  esac
}

setup_devices_hurd () {
  mkdir -p "$TARGET/servers/socket"
  /bin/settrans -cfk "$TARGET/servers/socket/1" /hurd/pflocal
  /bin/settrans -cf "$TARGET/servers/socket/2" /hurd/pfinet
  /bin/settrans -cfk "$TARGET/servers/exec" /hurd/exec
  /bin/settrans -cf "$TARGET/servers/crash-suspend" /hurd/crash --suspend
  /bin/settrans -cf "$TARGET/servers/crash-kill" /hurd/crash --kill
  /bin/settrans -cf "$TARGET/servers/crash-dump-core" /hurd/crash --dump-core
  /bin/settrans -cf "$TARGET/servers/password" /hurd/password
  /bin/settrans -cf "$TARGET/servers/default-pager" /hurd/proxy-defpager
  ln -fs crash-kill "$TARGET/servers/crash"
  ln -fs 1 "$TARGET/servers/socket/local"
  ln -fs 2 "$TARGET/servers/socket/inet"
  (cd "$TARGET/dev"; /sbin/MAKEDEV fd std ptyp ptyq vcs tty1 tty2 tty3 tty4 tty5 tty6)
}

setup_devices_fakechroot () {
  rm -rf "$TARGET/dev"
  ln -s /dev "$TARGET"
}

setup_dselect_method () {
  case "$1" in
    "apt")
      mkdir -p "$TARGET/var/lib/dpkg"
      echo "apt apt" > "$TARGET/var/lib/dpkg/cmethopt"
      chmod 644 "$TARGET/var/lib/dpkg/cmethopt"
      ;;
    *)
      error 1 UNKNOWNDSELECT "unknown dselect method"
      ;;
  esac
}

################################################################ pkgdetails

if [ -x /usr/bin/perl ]; then
  PKGDETAILS=pkgdetails_perl

  pkgdetails_field () {
      # uniq field mirror Packages values...
      perl -le '
$unique = shift @ARGV; $field = lc(shift @ARGV); $mirror = shift @ARGV;
$cnt = length(@ARGV);
%fields = map { $_, 0 } @ARGV;
while (<STDIN>) {
  chomp;
  next if (/^ /);
  if (/^([^:]*:)\s*(.*)$/) {
    $f = lc($1); $v = $2;
    $pkg = $v if ($f eq "package:");
    $ver = $v if ($f eq "version:");
    $arc = $v if ($f eq "architecture:");
    $fil = $v if ($f eq "filename:");
    $md5 = $v if ($f eq "md5sum:");
    $siz = $v if ($f eq "size:");
    $val = $v if ($f eq $field);
  } elsif (/^$/) {
    if (defined $val && defined $fields{$val}) {
      $cnt++;
      printf "%s %s %s %s %s %s %s\n",
             $pkg, $ver, $arc, $mirror, $fil, $md5, $siz;
      if ($unique) {
        delete $fields{$val};
        last if (--$cnt <= 0);
      }
    }
    undef $val;
  }
}
for $v (keys %fields) {
  printf ("%s -\n", $v) if ($unique);
}
' "$@"
  }

  pkgdetails_perl () {
    if [ "$1" = "WGET%" ]; then
      shift;
      perl -e '$v = 0; while (read STDIN, $x, 1) { if ($x =~ m/\d/) { $v *= 10; $v += $x; } elsif ($x eq "%") { printf "P: %d %d%s\n", int($v / 100.0 * ($ARGV[1] - $ARGV[0]) + $ARGV[0]), $ARGV[2], ($#ARGV == 3 ? " $ARGV[3]" : ""); $v = 0; } else { $v = 0; } }' "$@"
    elif [ "$1" = "GETDEPS" ]; then
      local pkgdest="$2"; shift; shift
      perl -e 'while (<STDIN>) { chomp; $in = 1 if (/^Package: (.*)$/ && grep {$_ eq $1} @ARGV); $in = 0 if (/^$/); if ($in and (/^Depends: (.*)$/ or /^Pre-Depends: (.*)$/)) { for $d (split /\s*,\s*/, $1) { $d =~ s/\s*[|].*$//; $d =~ s/\s*[(].*[)]\s*//; print "$d\n"; }}}' <"$pkgdest" "$@" | sort | uniq 
    elif [ "$1" = "PKGS" ]; then
      local m="$2"
      local p="$3"
      shift; shift; shift
      pkgdetails_field 1 Package: "$m" "$@" < "$p"
    elif [ "$1" = "FIELD" ]; then
      local f="$2"
      local m="$3"
      local p="$4"
      shift; shift; shift; shift
      pkgdetails_field 0 "$f" "$m" "$@" < "$p"
    fi
  }
elif [ -e "$DEBOOTSTRAP_DIR/pkgdetails" ]; then
  PKGDETAILS="$DEBOOTSTRAP_DIR/pkgdetails"
else
  PKGDETAILS=""
fi

##################################################### dependency resolution

resolve_deps () {
  local m1="${MIRRORS%% *}"

  # XXX: I can't think how to deal well with dependency resolution and
  #      lots of Packages files. -- aj 2005/06/12

  c="${COMPONENTS%% *}"
  local path="dists/$SUITE/$c/binary-$ARCH/Packages"
  local pkgdest="$TARGET/$($DLDEST pkg "$SUITE" "$c" "$ARCH" "$m1" "$path")"

  local PKGS="$*"
  local ALLPKGS="$PKGS"; 
  local ALLPKGS2="";
  while [ "$PKGS" != "" ]; do
    PKGS=$("$PKGDETAILS" GETDEPS "$pkgdest" $PKGS)
    PKGS=$("$PKGDETAILS" PKGS REAL "$pkgdest" $PKGS | sed -n 's/ .*REAL.*$//p')
    ALLPKGS2=$(echo "$PKGS $ALLPKGS" | tr ' ' '\n' | sort | uniq)
    PKGS=$(without "$ALLPKGS2" "$ALLPKGS")
    ALLPKGS="$ALLPKGS2"
  done
  echo $ALLPKGS
}

################################################################### helpers

read_gpg_status () {
  badsig=
  unkkey=
  validsig=
  while read prefix keyword keyid rest; do
    [ "$prefix" = '[GNUPG:]' ] || continue
    case $keyword in
      BADSIG) badsig="$keyid" ;;
      NO_PUBKEY) unkkey="$keyid" ;;
      VALIDSIG) validsig="$keyid" ;;
    esac
  done
  if [ "$validsig" ]; then
    info VALIDRELSIG "Valid Release signature (key id %s)" "$validsig"
  elif [ "$badsig" ]; then
    error 1 BADRELSIG "Invalid Release signature (key id %s)" "$badsig"
  elif [ "$unkkey" ]; then
    error 1 UNKNOWNRELSIG "Release signed by unknown key (key id %s)" "$unkkey"
  else
    error 1 SIGCHECK "Error executing gpgv to check Release signature"
  fi
}

without () {
  # usage:  without "a b c" "a d" -> "b" "c"
  (echo $1 | tr ' ' '\n' | sort | uniq;
   echo $2 $2 | tr ' ' '\n') | sort | uniq -u | tr '\n' ' '
  echo
}

repeat () {
  local n="$1"
  shift
  while [ "$n" -gt 0 ]; do
    if "$@"; then
      break
    else
      n="$(( $n - 1 ))"
      sleep 1
    fi
  done
  if [ "$n" -eq 0 ]; then return 1; fi
  return 0
}

N_EXIT_THINGS=0
exit_function () {
  local n=0
  while [ "$n" -lt "$N_EXIT_THINGS" ]; do
    (eval $(eval echo \${EXIT_THING_$n}) 2>/dev/null || true)
    n="$(( $n + 1 ))"
  done
  N_EXIT_THINGS=0
}

trap "exit_function" 0
trap "exit 129" 1
trap "error 130 INTERRUPTED \"Interrupt caught ... exiting\"" 2
trap "exit 131" 3
trap "exit 143" 15

on_exit () {
  eval `echo EXIT_THING_${N_EXIT_THINGS}=\"$1\"`
  N_EXIT_THINGS="$(( $N_EXIT_THINGS + 1 ))"
}

############################################################## fakechroot tools

install_fakechroot_tools () {
  mv "$TARGET/sbin/ldconfig" "$TARGET/sbin/ldconfig.REAL"
  echo \
"#!/bin/sh
echo
echo \"Warning: Fake ldconfig called, doing nothing\"" > "$TARGET/sbin/ldconfig"
  chmod 755 "$TARGET/sbin/ldconfig"

  echo \
"/sbin/ldconfig
/sbin/ldconfig.REAL
fakechroot" >> "$TARGET/var/lib/dpkg/diversions"

  mv "$TARGET/usr/bin/ldd" "$TARGET/usr/bin/ldd.REAL"
  cat << 'END' > "$TARGET/usr/bin/ldd"
#!/usr/bin/perl

# fakeldd
#
# Replacement for ldd with usage of objdump
#
# (c) 2003-2005 Piotr Roszatycki <dexter@debian.org>, BSD


my %libs = ();

my $status = 0;
my $dynamic = 0;
my $biarch = 0;

my $ldlinuxsodir = "/lib";
my @ld_library_path = qw(/usr/lib /lib);


sub ldso($) {
    my ($lib) = @_;
    my @files = ();

    if ($lib =~ /^\//) {
	$libs{$lib} = $lib;
	push @files, $lib;
    } else {
	foreach my $ld_path (@ld_library_path) {
	    next unless -f "$ld_path/$lib";
            my $badformat = 0;
	    open OBJDUMP, "objdump -p $ld_path/$lib 2>/dev/null |";
   	    while (my $line = <OBJDUMP>) {
        	if ($line =~ /file format (\S*)$/) {
		    $badformat = 1 unless $format eq $1;
		    last;
        	}
	    }
	    close OBJDUMP;
	    next if $badformat;
	    $libs{$lib} = "$ld_path/$lib";
	    push @files, "$ld_path/$lib";
	}
	objdump(@files);
    }
}


sub objdump(@) {
    my (@files) = @_;
    my @libs = ();

    foreach my $file (@files) {
	open OBJDUMP, "objdump -p $file 2>/dev/null |";
	while (my $line = <OBJDUMP>) {
	    $line =~ s/^\s+//;
	    my @f = split (/\s+/, $line);
    	    if ($line =~ /file format (\S*)$/) {
        	if (not $format) {
            	    $format = $1;
		    if ($unamearch eq "x86_64" and $format eq "elf32-i386") {
		        my $link = readlink "/lib/ld-linux.so.2";
			if ($link =~ /^\/emul\/ia32-linux\//) {
			    $ld_library_path[-2] = "/emul/ia32-linux/usr/lib";
			    $ld_library_path[-1] = "/emul/ia32-linux/lib";
			}
		    } elsif ($unamearch =~ /^(sparc|sparc64)$/ and $format eq "elf64-sparc") {
			$ldlinuxsodir = "/lib64";
			$ld_library_path[-2] = "/usr/lib64";
			$ld_library_path[-1] = "/lib64";
		    }
        	} else {
		    next unless $format eq $1;
        	}
	    }
	    if (not $dynamic and $f[0] eq "Dynamic") {
		$dynamic = 1;
	    }
	    next unless $f[0] eq "NEEDED";
	    if ($f[1] =~ /^ld-linux(\.|-)/) {
	        $f[1] = "$ldlinuxsodir/" . $f[1];
	    }
	    if (not defined $libs{$f[1]}) {
	        $libs{$f[1]} = undef;
		push @libs, $f[1];
	    }
	}
	close OBJDUMP;
    }

    foreach my $lib (@libs) {
	ldso($lib);
    }
}


if ($#ARGV < 0) {
    print STDERR "fakeldd: missing file arguments\n";
    exit 1;
}

while ($ARGV[0] =~ /^-/) {
    my $arg = $ARGV[0];
    shift @ARGV;
    last if $arg eq "--";
}

open LD_SO_CONF, "/etc/ld.so.conf";
while ($line = <LD_SO_CONF>) {
    chomp $line;
    unshift @ld_library_path, $line;
}
close LD_SO_CONF;

unshift @ld_library_path, split(/:/, $ENV{LD_LIBRARY_PATH});

$unamearch = `/bin/uname -m`;
chomp $unamearch;

foreach my $file (@ARGV) {
    my $address;
    %libs = ();
    $dynamic = 0;

    if ($#ARGV > 0) {
	print "$file:\n";
    }

    if (not -f $file) {
	print STDERR "ldd: $file: No such file or directory\n";
	$status = 1;
	next;
    }

    objdump($file);
    
    if ($dynamic == 0) {
	print "\tnot a dynamic executable\n";
	$status = 1;
    } elsif (scalar %libs eq "0") {
	print "\tstatically linked\n";
    }

    if ($format =~ /^elf64-/) {
        $address = "0x0000000000000000";
    } else {
        $address = "0x00000000";
    }

    foreach $lib (keys %libs) {
	if ($libs{$lib}) {
    	    printf "\t%s => %s (%s)\n", $lib, $libs{$lib}, $address;
	} else {
	    printf "\t%s => not found\n", $lib;
	}
    }
    
}

exit $status;
END
  chmod 755 "$TARGET/usr/bin/ldd"

  echo \
"/usr/bin/ldd
/usr/bin/ldd.REAL
fakechroot" >> "$TARGET/var/lib/dpkg/diversions"

}
